blob: 6ece53817034940927d3287d75cc36932351eacf [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/io.h>
15#include <linux/delay.h>
16#include <linux/platform_device.h>
17#include <linux/err.h>
18#include <linux/regulator/driver.h>
19#include <linux/regulator/machine.h>
20#include <linux/clk.h>
21#include <mach/msm_iomap.h>
22#include <mach/msm_bus_board.h>
23#include <mach/msm_bus.h>
24#include <mach/scm-io.h>
25#include "clock.h"
26#include "footswitch.h"
27
28#ifdef CONFIG_MSM_SECURE_IO
29#undef readl_relaxed
30#undef writel_relaxed
31#define readl_relaxed secure_readl
32#define writel_relaxed secure_writel
33#endif
34
35#define REG(off) (MSM_MMSS_CLK_CTL_BASE + (off))
36#define GEMINI_GFS_CTL_REG REG(0x01A0)
37#define GFX2D0_GFS_CTL_REG REG(0x0180)
38#define GFX2D1_GFS_CTL_REG REG(0x0184)
39#define GFX3D_GFS_CTL_REG REG(0x0188)
40#define MDP_GFS_CTL_REG REG(0x0190)
41#define ROT_GFS_CTL_REG REG(0x018C)
42#define VED_GFS_CTL_REG REG(0x0194)
43#define VFE_GFS_CTL_REG REG(0x0198)
44#define VPE_GFS_CTL_REG REG(0x019C)
45
46#define CLAMP_BIT BIT(5)
47#define ENABLE_BIT BIT(8)
48#define RETENTION_BIT BIT(9)
49
50#define RESET_DELAY_US 1
51/* Core clock rate to use if one has not previously been set. */
52#define DEFAULT_CLK_RATE 27000000
53
54/*
55 * Lock is only needed to protect against the first footswitch_enable()
56 * call occuring concurrently with late_footswitch_init().
57 */
58static DEFINE_MUTEX(claim_lock);
59
60struct clock_state {
61 int ahb_clk_en;
62 int axi_clk_en;
63 int core_clk_rate;
64};
65
66struct footswitch {
67 struct regulator_dev *rdev;
68 struct regulator_desc desc;
69 void *gfs_ctl_reg;
70 int bus_port1, bus_port2;
71 bool is_enabled;
72 bool is_claimed;
Matt Wagantall49722712011-08-17 18:50:53 -070073 const bool has_axi_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074 struct clk *core_clk;
75 struct clk *ahb_clk;
76 struct clk *axi_clk;
77 unsigned int reset_rate;
78 struct clock_state clk_state;
79 unsigned int gfs_delay_cnt:5;
80};
81
82static int setup_clocks(struct footswitch *fs)
83{
84 int rc = 0;
85
86 /*
87 * Enable all clocks in the power domain. If a core requires a
88 * specific clock rate when being reset, apply it.
89 */
90 fs->clk_state.core_clk_rate = clk_get_rate(fs->core_clk);
91 if (!fs->clk_state.core_clk_rate || fs->reset_rate) {
92 int rate = fs->reset_rate ? fs->reset_rate : DEFAULT_CLK_RATE;
93 rc = clk_set_rate(fs->core_clk, rate);
94 if (rc) {
Matt Wagantall49722712011-08-17 18:50:53 -070095 pr_err("%s: Failed to set core_clk rate to %d Hz.\n",
96 __func__, fs->reset_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097 return rc;
98 }
99 }
100 clk_enable(fs->core_clk);
101
102 /*
103 * Some AHB and AXI clocks are for reset purposes only. These clocks
104 * will fail to enable. Keep track of them so we don't try to disable
105 * them later and crash.
106 */
107 fs->clk_state.ahb_clk_en = !clk_enable(fs->ahb_clk);
108 if (fs->axi_clk)
109 fs->clk_state.axi_clk_en = !clk_enable(fs->axi_clk);
110
111 return rc;
112}
113
114static void restore_clocks(struct footswitch *fs)
115{
116 /* Restore clocks to their orignal states before setup_clocks(). */
117 if (fs->axi_clk && fs->clk_state.axi_clk_en)
118 clk_disable(fs->axi_clk);
119 if (fs->clk_state.ahb_clk_en)
120 clk_disable(fs->ahb_clk);
121 clk_disable(fs->core_clk);
122 if (fs->clk_state.core_clk_rate) {
123 if (clk_set_rate(fs->core_clk, fs->clk_state.core_clk_rate))
Matt Wagantall49722712011-08-17 18:50:53 -0700124 pr_err("%s: Failed to restore core_clk rate.\n",
125 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 }
127}
128
129static int footswitch_is_enabled(struct regulator_dev *rdev)
130{
131 struct footswitch *fs = rdev_get_drvdata(rdev);
132
133 return fs->is_enabled;
134}
135
136static int footswitch_enable(struct regulator_dev *rdev)
137{
138 struct footswitch *fs = rdev_get_drvdata(rdev);
139 uint32_t regval, rc = 0;
140
141 mutex_lock(&claim_lock);
142 fs->is_claimed = true;
143 mutex_unlock(&claim_lock);
144
Matt Wagantall88edea92011-07-21 10:29:56 -0700145 /* Return early if already enabled. */
146 regval = readl_relaxed(fs->gfs_ctl_reg);
147 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
148 return 0;
149
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 /* Make sure required clocks are on at the correct rates. */
151 rc = setup_clocks(fs);
152 if (rc)
153 goto out;
154
155 /* Un-halt all bus ports in the power domain. */
156 if (fs->bus_port1) {
157 rc = msm_bus_axi_portunhalt(fs->bus_port1);
158 if (rc) {
159 pr_err("%s: Port 1 unhalt failed.\n", __func__);
160 goto out;
161 }
162 }
163 if (fs->bus_port2) {
164 rc = msm_bus_axi_portunhalt(fs->bus_port2);
165 if (rc) {
166 pr_err("%s: Port 2 unhalt failed.\n", __func__);
167 goto out;
168 }
169 }
170
171 /*
172 * (Re-)Assert resets for all clocks in the clock domain, since
173 * footswitch_enable() is first called before footswitch_disable()
174 * and resets should be asserted before power is restored.
175 */
176 if (fs->axi_clk)
177 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
178 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
179 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
180 /* Wait for synchronous resets to propagate. */
181 udelay(RESET_DELAY_US);
182
183 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 regval |= ENABLE_BIT;
185 writel_relaxed(regval, fs->gfs_ctl_reg);
186 /* Wait for the rail to fully charge. */
187 mb();
188 udelay(1);
189
190 /* Un-clamp the I/O ports. */
191 regval &= ~CLAMP_BIT;
192 writel_relaxed(regval, fs->gfs_ctl_reg);
193
194 /* Deassert resets for all clocks in the power domain. */
195 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
196 clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
197 if (fs->axi_clk)
198 clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
199 /* Toggle core reset again after first power-on (required for GFX3D). */
200 if (fs->desc.id == FS_GFX3D) {
201 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
202 udelay(RESET_DELAY_US);
203 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
204 udelay(RESET_DELAY_US);
205 }
206
207 /* Return clocks to their state before this function. */
208 restore_clocks(fs);
209
210 fs->is_enabled = true;
211out:
212 return rc;
213}
214
215static int footswitch_disable(struct regulator_dev *rdev)
216{
217 struct footswitch *fs = rdev_get_drvdata(rdev);
218 uint32_t regval, rc = 0;
219
Matt Wagantall88edea92011-07-21 10:29:56 -0700220 /* Return early if already disabled. */
221 regval = readl_relaxed(fs->gfs_ctl_reg);
222 if ((regval & ENABLE_BIT) == 0)
223 return 0;
224
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 /* Make sure required clocks are on at the correct rates. */
226 rc = setup_clocks(fs);
227 if (rc)
228 goto out;
229
230 /* Halt all bus ports in the power domain. */
231 if (fs->bus_port1) {
232 rc = msm_bus_axi_porthalt(fs->bus_port1);
233 if (rc) {
234 pr_err("%s: Port 1 halt failed.\n", __func__);
235 goto out;
236 }
237 }
238 if (fs->bus_port2) {
239 rc = msm_bus_axi_porthalt(fs->bus_port2);
240 if (rc) {
241 pr_err("%s: Port 1 halt failed.\n", __func__);
242 goto err_port2_halt;
243 }
244 }
245
246 /*
247 * Assert resets for all clocks in the clock domain so that
248 * outputs settle prior to clamping.
249 */
250 if (fs->axi_clk)
251 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
252 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
253 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
254 /* Wait for synchronous resets to propagate. */
255 udelay(RESET_DELAY_US);
256
257 /*
258 * Clamp the I/O ports of the core to ensure the values
259 * remain fixed while the core is collapsed.
260 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 regval |= CLAMP_BIT;
262 writel_relaxed(regval, fs->gfs_ctl_reg);
263
264 /* Collapse the power rail at the footswitch. */
265 regval &= ~ENABLE_BIT;
266 writel_relaxed(regval, fs->gfs_ctl_reg);
267
268 /* Return clocks to their state before this function. */
269 restore_clocks(fs);
270
271 fs->is_enabled = false;
272
273 return rc;
274
275err_port2_halt:
276 msm_bus_axi_portunhalt(fs->bus_port1);
277out:
278 return rc;
279}
280
281static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
282{
283 struct footswitch *fs = rdev_get_drvdata(rdev);
284 uint32_t regval, rc = 0;
285
286 mutex_lock(&claim_lock);
287 fs->is_claimed = true;
288 mutex_unlock(&claim_lock);
289
Matt Wagantall88edea92011-07-21 10:29:56 -0700290 /* Return early if already enabled. */
291 regval = readl_relaxed(fs->gfs_ctl_reg);
292 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
293 return 0;
294
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 /* Make sure required clocks are on at the correct rates. */
296 rc = setup_clocks(fs);
297 if (rc)
298 goto out;
299
300 /* Un-halt all bus ports in the power domain. */
301 if (fs->bus_port1) {
302 rc = msm_bus_axi_portunhalt(fs->bus_port1);
303 if (rc) {
304 pr_err("%s: Port 1 unhalt failed.\n", __func__);
305 goto out;
306 }
307 }
308
309 /* Disable core clock. */
310 clk_disable(fs->core_clk);
311
312 /*
313 * (Re-)Assert resets for all clocks in the clock domain, since
314 * footswitch_enable() is first called before footswitch_disable()
315 * and resets should be asserted before power is restored.
316 */
317 if (fs->axi_clk)
318 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
319 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
320 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
321 /* Wait for synchronous resets to propagate. */
322 udelay(20);
323
324 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 regval |= ENABLE_BIT;
326 writel_relaxed(regval, fs->gfs_ctl_reg);
327 mb();
328 udelay(1);
329
330 /* Un-clamp the I/O ports. */
331 regval &= ~CLAMP_BIT;
332 writel_relaxed(regval, fs->gfs_ctl_reg);
333
334 /* Deassert resets for all clocks in the power domain. */
335 if (fs->axi_clk)
336 clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
337 clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
338 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
339 udelay(20);
340
341 /* Re-enable core clock. */
342 clk_enable(fs->core_clk);
343
344 /* Return clocks to their state before this function. */
345 restore_clocks(fs);
346
347 fs->is_enabled = true;
348out:
349 return rc;
350}
351
352static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
353{
354 struct footswitch *fs = rdev_get_drvdata(rdev);
355 uint32_t regval, rc = 0;
356
Matt Wagantall88edea92011-07-21 10:29:56 -0700357 /* Return early if already disabled. */
358 regval = readl_relaxed(fs->gfs_ctl_reg);
359 if ((regval & ENABLE_BIT) == 0)
360 return 0;
361
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 /* Make sure required clocks are on at the correct rates. */
363 rc = setup_clocks(fs);
364 if (rc)
365 goto out;
366
367 /* Halt all bus ports in the power domain. */
368 if (fs->bus_port1) {
369 rc = msm_bus_axi_porthalt(fs->bus_port1);
370 if (rc) {
371 pr_err("%s: Port 1 halt failed.\n", __func__);
372 goto out;
373 }
374 }
375
376 /* Disable core clock. */
377 clk_disable(fs->core_clk);
378
379 /*
380 * Assert resets for all clocks in the clock domain so that
381 * outputs settle prior to clamping.
382 */
383 if (fs->axi_clk)
384 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
385 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
386 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
387 /* Wait for synchronous resets to propagate. */
388 udelay(20);
389
390 /*
391 * Clamp the I/O ports of the core to ensure the values
392 * remain fixed while the core is collapsed.
393 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 regval |= CLAMP_BIT;
395 writel_relaxed(regval, fs->gfs_ctl_reg);
396
397 /* Collapse the power rail at the footswitch. */
398 regval &= ~ENABLE_BIT;
399 writel_relaxed(regval, fs->gfs_ctl_reg);
400
401 /* Re-enable core clock. */
402 clk_enable(fs->core_clk);
403
404 /* Return clocks to their state before this function. */
405 restore_clocks(fs);
406
407 fs->is_enabled = false;
408
409out:
410 return rc;
411}
412
413static struct regulator_ops standard_fs_ops = {
414 .is_enabled = footswitch_is_enabled,
415 .enable = footswitch_enable,
416 .disable = footswitch_disable,
417};
418
419static struct regulator_ops gfx2d_fs_ops = {
420 .is_enabled = footswitch_is_enabled,
421 .enable = gfx2d_footswitch_enable,
422 .disable = gfx2d_footswitch_disable,
423};
424
Matt Wagantall49722712011-08-17 18:50:53 -0700425#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg, _dc, _axi_clk, \
426 _reset_rate, _bp1, _bp2) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427 [(_id)] = { \
428 .desc = { \
429 .id = (_id), \
430 .name = (_name), \
431 .ops = (_ops), \
432 .type = REGULATOR_VOLTAGE, \
433 .owner = THIS_MODULE, \
434 }, \
435 .gfs_ctl_reg = (_gfs_ctl_reg), \
436 .gfs_delay_cnt = (_dc), \
437 .bus_port1 = (_bp1), \
438 .bus_port2 = (_bp2), \
Matt Wagantall49722712011-08-17 18:50:53 -0700439 .has_axi_clk = (_axi_clk), \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 .reset_rate = (_reset_rate), \
441 }
442static struct footswitch footswitches[] = {
443 FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700444 GFX2D0_GFS_CTL_REG, 31, false, 0,
445 MSM_BUS_MASTER_GRAPHICS_2D_CORE0, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446 FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700447 GFX2D1_GFS_CTL_REG, 31, false, 0,
448 MSM_BUS_MASTER_GRAPHICS_2D_CORE1, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700450 GFX3D_GFS_CTL_REG, 31, false, 27000000,
451 MSM_BUS_MASTER_GRAPHICS_3D, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700453 GEMINI_GFS_CTL_REG, 31, true, 0,
454 MSM_BUS_MASTER_JPEG_ENC, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700456 MDP_GFS_CTL_REG, 31, true, 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 MSM_BUS_MASTER_MDP_PORT0,
Matt Wagantall49722712011-08-17 18:50:53 -0700458 MSM_BUS_MASTER_MDP_PORT1),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459 FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700460 ROT_GFS_CTL_REG, 31, true, 0,
461 MSM_BUS_MASTER_ROTATOR, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462 FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700463 VED_GFS_CTL_REG, 31, true, 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 MSM_BUS_MASTER_HD_CODEC_PORT0,
Matt Wagantall49722712011-08-17 18:50:53 -0700465 MSM_BUS_MASTER_HD_CODEC_PORT1),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466 FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700467 VFE_GFS_CTL_REG, 31, true, 0,
468 MSM_BUS_MASTER_VFE, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469 FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700470 VPE_GFS_CTL_REG, 31, true, 0,
471 MSM_BUS_MASTER_VPE, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472};
473
474static int footswitch_probe(struct platform_device *pdev)
475{
476 struct footswitch *fs;
477 struct regulator_init_data *init_data;
478 uint32_t regval, rc = 0;
479
480 if (pdev == NULL)
481 return -EINVAL;
482
483 if (pdev->id >= MAX_FS)
484 return -ENODEV;
485
486 fs = &footswitches[pdev->id];
487 init_data = pdev->dev.platform_data;
488
489 /* Setup core clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700490 fs->core_clk = clk_get(&pdev->dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 if (IS_ERR(fs->core_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700492 pr_err("%s: clk_get(core_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493 rc = PTR_ERR(fs->core_clk);
494 goto err_core_clk;
495 }
496
497 /* Setup AHB clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700498 fs->ahb_clk = clk_get(&pdev->dev, "iface_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700499 if (IS_ERR(fs->ahb_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700500 pr_err("%s: clk_get(iface_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700501 rc = PTR_ERR(fs->ahb_clk);
502 goto err_ahb_clk;
503 }
504
505 /* Setup AXI clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700506 if (fs->has_axi_clk) {
507 fs->axi_clk = clk_get(&pdev->dev, "bus_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508 if (IS_ERR(fs->axi_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700509 pr_err("%s: clk_get(bus_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 rc = PTR_ERR(fs->axi_clk);
511 goto err_axi_clk;
512 }
513 }
514
515 /*
516 * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all
517 * after enabling the footswitch. Also ensure the retention bit is
518 * clear so disabling the footswitch will power-collapse the core.
519 */
520 regval = readl_relaxed(fs->gfs_ctl_reg);
521 regval |= fs->gfs_delay_cnt;
522 regval &= ~RETENTION_BIT;
523 writel_relaxed(regval, fs->gfs_ctl_reg);
524
525 fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs);
526 if (IS_ERR(footswitches[pdev->id].rdev)) {
527 pr_err("%s: regulator_register(\"%s\") failed\n",
528 __func__, fs->desc.name);
529 rc = PTR_ERR(footswitches[pdev->id].rdev);
530 goto err_register;
531 }
532
533 return 0;
534
535err_register:
Matt Wagantall49722712011-08-17 18:50:53 -0700536 if (fs->has_axi_clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 clk_put(fs->axi_clk);
538err_axi_clk:
539 clk_put(fs->ahb_clk);
540err_ahb_clk:
541 clk_put(fs->core_clk);
542err_core_clk:
543 return rc;
544}
545
546static int __devexit footswitch_remove(struct platform_device *pdev)
547{
548 struct footswitch *fs = &footswitches[pdev->id];
549
550 clk_put(fs->core_clk);
551 clk_put(fs->ahb_clk);
552 if (fs->axi_clk)
553 clk_put(fs->axi_clk);
554
555 regulator_unregister(fs->rdev);
556
557 return 0;
558}
559
560static struct platform_driver footswitch_driver = {
561 .probe = footswitch_probe,
562 .remove = __devexit_p(footswitch_remove),
563 .driver = {
Matt Wagantall49722712011-08-17 18:50:53 -0700564 .name = "footswitch-8x60",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700565 .owner = THIS_MODULE,
566 },
567};
568
569static int __init late_footswitch_init(void)
570{
571 int i;
572
573 mutex_lock(&claim_lock);
574 /* Turn off all registered but unused footswitches. */
575 for (i = 0; i < ARRAY_SIZE(footswitches); i++)
576 if (footswitches[i].rdev && !footswitches[i].is_claimed)
Matt Wagantall7a261362011-07-14 19:07:10 -0700577 footswitches[i].rdev->desc->ops->
578 disable(footswitches[i].rdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 mutex_unlock(&claim_lock);
580
581 return 0;
582}
583late_initcall(late_footswitch_init);
584
585static int __init footswitch_init(void)
586{
587 return platform_driver_register(&footswitch_driver);
588}
589subsys_initcall(footswitch_init);
590
591static void __exit footswitch_exit(void)
592{
593 platform_driver_unregister(&footswitch_driver);
594}
595module_exit(footswitch_exit);
596
597MODULE_LICENSE("GPL v2");
598MODULE_DESCRIPTION("MSM8x60 rail footswitch");
599MODULE_ALIAS("platform:footswitch-msm8x60");