blob: 500641920d8d6f30c870a0d270e53eaef975ece6 [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)
Matt Wagantall37f34b32011-08-23 18:14:47 -070045#define VCAP_GFS_CTL_REG REG(0x0254)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
47#define CLAMP_BIT BIT(5)
48#define ENABLE_BIT BIT(8)
49#define RETENTION_BIT BIT(9)
50
51#define RESET_DELAY_US 1
52/* Core clock rate to use if one has not previously been set. */
53#define DEFAULT_CLK_RATE 27000000
54
55/*
56 * Lock is only needed to protect against the first footswitch_enable()
57 * call occuring concurrently with late_footswitch_init().
58 */
59static DEFINE_MUTEX(claim_lock);
60
61struct clock_state {
62 int ahb_clk_en;
63 int axi_clk_en;
64 int core_clk_rate;
65};
66
67struct footswitch {
68 struct regulator_dev *rdev;
69 struct regulator_desc desc;
70 void *gfs_ctl_reg;
71 int bus_port1, bus_port2;
72 bool is_enabled;
73 bool is_claimed;
Matt Wagantall49722712011-08-17 18:50:53 -070074 const bool has_axi_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075 struct clk *core_clk;
76 struct clk *ahb_clk;
77 struct clk *axi_clk;
78 unsigned int reset_rate;
79 struct clock_state clk_state;
80 unsigned int gfs_delay_cnt:5;
81};
82
83static int setup_clocks(struct footswitch *fs)
84{
85 int rc = 0;
86
87 /*
88 * Enable all clocks in the power domain. If a core requires a
89 * specific clock rate when being reset, apply it.
90 */
91 fs->clk_state.core_clk_rate = clk_get_rate(fs->core_clk);
92 if (!fs->clk_state.core_clk_rate || fs->reset_rate) {
93 int rate = fs->reset_rate ? fs->reset_rate : DEFAULT_CLK_RATE;
94 rc = clk_set_rate(fs->core_clk, rate);
95 if (rc) {
Matt Wagantall49722712011-08-17 18:50:53 -070096 pr_err("%s: Failed to set core_clk rate to %d Hz.\n",
97 __func__, fs->reset_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 return rc;
99 }
100 }
101 clk_enable(fs->core_clk);
102
103 /*
104 * Some AHB and AXI clocks are for reset purposes only. These clocks
105 * will fail to enable. Keep track of them so we don't try to disable
106 * them later and crash.
107 */
108 fs->clk_state.ahb_clk_en = !clk_enable(fs->ahb_clk);
109 if (fs->axi_clk)
110 fs->clk_state.axi_clk_en = !clk_enable(fs->axi_clk);
111
112 return rc;
113}
114
115static void restore_clocks(struct footswitch *fs)
116{
117 /* Restore clocks to their orignal states before setup_clocks(). */
118 if (fs->axi_clk && fs->clk_state.axi_clk_en)
119 clk_disable(fs->axi_clk);
120 if (fs->clk_state.ahb_clk_en)
121 clk_disable(fs->ahb_clk);
122 clk_disable(fs->core_clk);
123 if (fs->clk_state.core_clk_rate) {
124 if (clk_set_rate(fs->core_clk, fs->clk_state.core_clk_rate))
Matt Wagantall49722712011-08-17 18:50:53 -0700125 pr_err("%s: Failed to restore core_clk rate.\n",
126 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 }
128}
129
130static int footswitch_is_enabled(struct regulator_dev *rdev)
131{
132 struct footswitch *fs = rdev_get_drvdata(rdev);
133
134 return fs->is_enabled;
135}
136
137static int footswitch_enable(struct regulator_dev *rdev)
138{
139 struct footswitch *fs = rdev_get_drvdata(rdev);
140 uint32_t regval, rc = 0;
141
142 mutex_lock(&claim_lock);
143 fs->is_claimed = true;
144 mutex_unlock(&claim_lock);
145
Matt Wagantall88edea92011-07-21 10:29:56 -0700146 /* Return early if already enabled. */
147 regval = readl_relaxed(fs->gfs_ctl_reg);
148 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
149 return 0;
150
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 /* Make sure required clocks are on at the correct rates. */
152 rc = setup_clocks(fs);
153 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800154 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155
156 /* Un-halt all bus ports in the power domain. */
157 if (fs->bus_port1) {
158 rc = msm_bus_axi_portunhalt(fs->bus_port1);
159 if (rc) {
160 pr_err("%s: Port 1 unhalt failed.\n", __func__);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800161 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 }
163 }
164 if (fs->bus_port2) {
165 rc = msm_bus_axi_portunhalt(fs->bus_port2);
166 if (rc) {
167 pr_err("%s: Port 2 unhalt failed.\n", __func__);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800168 goto err_port2_halt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 }
170 }
171
172 /*
173 * (Re-)Assert resets for all clocks in the clock domain, since
174 * footswitch_enable() is first called before footswitch_disable()
175 * and resets should be asserted before power is restored.
176 */
177 if (fs->axi_clk)
178 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
179 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
180 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
181 /* Wait for synchronous resets to propagate. */
182 udelay(RESET_DELAY_US);
183
184 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 regval |= ENABLE_BIT;
186 writel_relaxed(regval, fs->gfs_ctl_reg);
187 /* Wait for the rail to fully charge. */
188 mb();
189 udelay(1);
190
191 /* Un-clamp the I/O ports. */
192 regval &= ~CLAMP_BIT;
193 writel_relaxed(regval, fs->gfs_ctl_reg);
194
195 /* Deassert resets for all clocks in the power domain. */
196 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
197 clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
198 if (fs->axi_clk)
199 clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
200 /* Toggle core reset again after first power-on (required for GFX3D). */
201 if (fs->desc.id == FS_GFX3D) {
202 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
203 udelay(RESET_DELAY_US);
204 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
205 udelay(RESET_DELAY_US);
206 }
207
208 /* Return clocks to their state before this function. */
209 restore_clocks(fs);
210
211 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800212 return 0;
213
214err_port2_halt:
215 msm_bus_axi_porthalt(fs->bus_port1);
216err:
217 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 return rc;
219}
220
221static int footswitch_disable(struct regulator_dev *rdev)
222{
223 struct footswitch *fs = rdev_get_drvdata(rdev);
224 uint32_t regval, rc = 0;
225
Matt Wagantall88edea92011-07-21 10:29:56 -0700226 /* Return early if already disabled. */
227 regval = readl_relaxed(fs->gfs_ctl_reg);
228 if ((regval & ENABLE_BIT) == 0)
229 return 0;
230
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231 /* Make sure required clocks are on at the correct rates. */
232 rc = setup_clocks(fs);
233 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800234 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235
236 /* Halt all bus ports in the power domain. */
237 if (fs->bus_port1) {
238 rc = msm_bus_axi_porthalt(fs->bus_port1);
239 if (rc) {
240 pr_err("%s: Port 1 halt failed.\n", __func__);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800241 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 }
243 }
244 if (fs->bus_port2) {
245 rc = msm_bus_axi_porthalt(fs->bus_port2);
246 if (rc) {
247 pr_err("%s: Port 1 halt failed.\n", __func__);
248 goto err_port2_halt;
249 }
250 }
251
252 /*
253 * Assert resets for all clocks in the clock domain so that
254 * outputs settle prior to clamping.
255 */
256 if (fs->axi_clk)
257 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
258 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
259 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
260 /* Wait for synchronous resets to propagate. */
261 udelay(RESET_DELAY_US);
262
263 /*
Matt Wagantall1ab7d942011-12-02 17:59:57 -0800264 * Return clocks to their state before this function. For robustness
265 * if memory-retention across collapses is required, clocks should
266 * be disabled before asserting the clamps. Assuming clocks were off
267 * before entering footswitch_disable(), this will be true.
268 */
269 restore_clocks(fs);
270
271 /*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 * Clamp the I/O ports of the core to ensure the values
273 * remain fixed while the core is collapsed.
274 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 regval |= CLAMP_BIT;
276 writel_relaxed(regval, fs->gfs_ctl_reg);
277
278 /* Collapse the power rail at the footswitch. */
279 regval &= ~ENABLE_BIT;
280 writel_relaxed(regval, fs->gfs_ctl_reg);
281
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800283 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284
285err_port2_halt:
286 msm_bus_axi_portunhalt(fs->bus_port1);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800287err:
288 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 return rc;
290}
291
292static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
293{
294 struct footswitch *fs = rdev_get_drvdata(rdev);
295 uint32_t regval, rc = 0;
296
297 mutex_lock(&claim_lock);
298 fs->is_claimed = true;
299 mutex_unlock(&claim_lock);
300
Matt Wagantall88edea92011-07-21 10:29:56 -0700301 /* Return early if already enabled. */
302 regval = readl_relaxed(fs->gfs_ctl_reg);
303 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
304 return 0;
305
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306 /* Make sure required clocks are on at the correct rates. */
307 rc = setup_clocks(fs);
308 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800309 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310
311 /* Un-halt all bus ports in the power domain. */
312 if (fs->bus_port1) {
313 rc = msm_bus_axi_portunhalt(fs->bus_port1);
314 if (rc) {
315 pr_err("%s: Port 1 unhalt failed.\n", __func__);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800316 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 }
318 }
319
320 /* Disable core clock. */
321 clk_disable(fs->core_clk);
322
323 /*
324 * (Re-)Assert resets for all clocks in the clock domain, since
325 * footswitch_enable() is first called before footswitch_disable()
326 * and resets should be asserted before power is restored.
327 */
328 if (fs->axi_clk)
329 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
330 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
331 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
332 /* Wait for synchronous resets to propagate. */
333 udelay(20);
334
335 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336 regval |= ENABLE_BIT;
337 writel_relaxed(regval, fs->gfs_ctl_reg);
338 mb();
339 udelay(1);
340
341 /* Un-clamp the I/O ports. */
342 regval &= ~CLAMP_BIT;
343 writel_relaxed(regval, fs->gfs_ctl_reg);
344
345 /* Deassert resets for all clocks in the power domain. */
346 if (fs->axi_clk)
347 clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
348 clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
349 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
350 udelay(20);
351
352 /* Re-enable core clock. */
353 clk_enable(fs->core_clk);
354
355 /* Return clocks to their state before this function. */
356 restore_clocks(fs);
357
358 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800359 return 0;
360
361err:
362 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363 return rc;
364}
365
366static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
367{
368 struct footswitch *fs = rdev_get_drvdata(rdev);
369 uint32_t regval, rc = 0;
370
Matt Wagantall88edea92011-07-21 10:29:56 -0700371 /* Return early if already disabled. */
372 regval = readl_relaxed(fs->gfs_ctl_reg);
373 if ((regval & ENABLE_BIT) == 0)
374 return 0;
375
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 /* Make sure required clocks are on at the correct rates. */
377 rc = setup_clocks(fs);
378 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800379 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380
381 /* Halt all bus ports in the power domain. */
382 if (fs->bus_port1) {
383 rc = msm_bus_axi_porthalt(fs->bus_port1);
384 if (rc) {
385 pr_err("%s: Port 1 halt failed.\n", __func__);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800386 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 }
388 }
389
390 /* Disable core clock. */
391 clk_disable(fs->core_clk);
392
393 /*
394 * Assert resets for all clocks in the clock domain so that
395 * outputs settle prior to clamping.
396 */
397 if (fs->axi_clk)
398 clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
399 clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
400 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
401 /* Wait for synchronous resets to propagate. */
402 udelay(20);
403
404 /*
405 * Clamp the I/O ports of the core to ensure the values
406 * remain fixed while the core is collapsed.
407 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 regval |= CLAMP_BIT;
409 writel_relaxed(regval, fs->gfs_ctl_reg);
410
411 /* Collapse the power rail at the footswitch. */
412 regval &= ~ENABLE_BIT;
413 writel_relaxed(regval, fs->gfs_ctl_reg);
414
415 /* Re-enable core clock. */
416 clk_enable(fs->core_clk);
417
418 /* Return clocks to their state before this function. */
419 restore_clocks(fs);
420
421 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800422 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800424err:
425 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426 return rc;
427}
428
429static struct regulator_ops standard_fs_ops = {
430 .is_enabled = footswitch_is_enabled,
431 .enable = footswitch_enable,
432 .disable = footswitch_disable,
433};
434
435static struct regulator_ops gfx2d_fs_ops = {
436 .is_enabled = footswitch_is_enabled,
437 .enable = gfx2d_footswitch_enable,
438 .disable = gfx2d_footswitch_disable,
439};
440
Matt Wagantall49722712011-08-17 18:50:53 -0700441#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg, _dc, _axi_clk, \
442 _reset_rate, _bp1, _bp2) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443 [(_id)] = { \
444 .desc = { \
445 .id = (_id), \
446 .name = (_name), \
447 .ops = (_ops), \
448 .type = REGULATOR_VOLTAGE, \
449 .owner = THIS_MODULE, \
450 }, \
451 .gfs_ctl_reg = (_gfs_ctl_reg), \
452 .gfs_delay_cnt = (_dc), \
453 .bus_port1 = (_bp1), \
454 .bus_port2 = (_bp2), \
Matt Wagantall49722712011-08-17 18:50:53 -0700455 .has_axi_clk = (_axi_clk), \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456 .reset_rate = (_reset_rate), \
457 }
458static struct footswitch footswitches[] = {
459 FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700460 GFX2D0_GFS_CTL_REG, 31, false, 0,
461 MSM_BUS_MASTER_GRAPHICS_2D_CORE0, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462 FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700463 GFX2D1_GFS_CTL_REG, 31, false, 0,
464 MSM_BUS_MASTER_GRAPHICS_2D_CORE1, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700466 GFX3D_GFS_CTL_REG, 31, false, 27000000,
467 MSM_BUS_MASTER_GRAPHICS_3D, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468 FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700469 GEMINI_GFS_CTL_REG, 31, true, 0,
470 MSM_BUS_MASTER_JPEG_ENC, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471 FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700472 MDP_GFS_CTL_REG, 31, true, 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 MSM_BUS_MASTER_MDP_PORT0,
Matt Wagantall49722712011-08-17 18:50:53 -0700474 MSM_BUS_MASTER_MDP_PORT1),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475 FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700476 ROT_GFS_CTL_REG, 31, true, 0,
477 MSM_BUS_MASTER_ROTATOR, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478 FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700479 VED_GFS_CTL_REG, 31, true, 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480 MSM_BUS_MASTER_HD_CODEC_PORT0,
Matt Wagantall49722712011-08-17 18:50:53 -0700481 MSM_BUS_MASTER_HD_CODEC_PORT1),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482 FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700483 VFE_GFS_CTL_REG, 31, true, 0,
484 MSM_BUS_MASTER_VFE, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700485 FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops,
Matt Wagantall49722712011-08-17 18:50:53 -0700486 VPE_GFS_CTL_REG, 31, true, 0,
487 MSM_BUS_MASTER_VPE, 0),
Matt Wagantall37f34b32011-08-23 18:14:47 -0700488 FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops,
489 VCAP_GFS_CTL_REG, 31, true, 0,
490 MSM_BUS_MASTER_VIDEO_CAP, 0),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491};
492
493static int footswitch_probe(struct platform_device *pdev)
494{
495 struct footswitch *fs;
496 struct regulator_init_data *init_data;
497 uint32_t regval, rc = 0;
498
499 if (pdev == NULL)
500 return -EINVAL;
501
502 if (pdev->id >= MAX_FS)
503 return -ENODEV;
504
505 fs = &footswitches[pdev->id];
506 init_data = pdev->dev.platform_data;
507
508 /* Setup core clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700509 fs->core_clk = clk_get(&pdev->dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 if (IS_ERR(fs->core_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700511 pr_err("%s: clk_get(core_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700512 rc = PTR_ERR(fs->core_clk);
513 goto err_core_clk;
514 }
515
516 /* Setup AHB clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700517 fs->ahb_clk = clk_get(&pdev->dev, "iface_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 if (IS_ERR(fs->ahb_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700519 pr_err("%s: clk_get(iface_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 rc = PTR_ERR(fs->ahb_clk);
521 goto err_ahb_clk;
522 }
523
524 /* Setup AXI clock. */
Matt Wagantall49722712011-08-17 18:50:53 -0700525 if (fs->has_axi_clk) {
526 fs->axi_clk = clk_get(&pdev->dev, "bus_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 if (IS_ERR(fs->axi_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700528 pr_err("%s: clk_get(bus_clk) failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529 rc = PTR_ERR(fs->axi_clk);
530 goto err_axi_clk;
531 }
532 }
533
534 /*
535 * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all
536 * after enabling the footswitch. Also ensure the retention bit is
537 * clear so disabling the footswitch will power-collapse the core.
538 */
539 regval = readl_relaxed(fs->gfs_ctl_reg);
540 regval |= fs->gfs_delay_cnt;
541 regval &= ~RETENTION_BIT;
542 writel_relaxed(regval, fs->gfs_ctl_reg);
543
544 fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs);
545 if (IS_ERR(footswitches[pdev->id].rdev)) {
546 pr_err("%s: regulator_register(\"%s\") failed\n",
547 __func__, fs->desc.name);
548 rc = PTR_ERR(footswitches[pdev->id].rdev);
549 goto err_register;
550 }
551
552 return 0;
553
554err_register:
Matt Wagantall49722712011-08-17 18:50:53 -0700555 if (fs->has_axi_clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 clk_put(fs->axi_clk);
557err_axi_clk:
558 clk_put(fs->ahb_clk);
559err_ahb_clk:
560 clk_put(fs->core_clk);
561err_core_clk:
562 return rc;
563}
564
565static int __devexit footswitch_remove(struct platform_device *pdev)
566{
567 struct footswitch *fs = &footswitches[pdev->id];
568
569 clk_put(fs->core_clk);
570 clk_put(fs->ahb_clk);
571 if (fs->axi_clk)
572 clk_put(fs->axi_clk);
573
574 regulator_unregister(fs->rdev);
575
576 return 0;
577}
578
579static struct platform_driver footswitch_driver = {
580 .probe = footswitch_probe,
581 .remove = __devexit_p(footswitch_remove),
582 .driver = {
Matt Wagantall49722712011-08-17 18:50:53 -0700583 .name = "footswitch-8x60",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700584 .owner = THIS_MODULE,
585 },
586};
587
588static int __init late_footswitch_init(void)
589{
590 int i;
591
592 mutex_lock(&claim_lock);
593 /* Turn off all registered but unused footswitches. */
594 for (i = 0; i < ARRAY_SIZE(footswitches); i++)
595 if (footswitches[i].rdev && !footswitches[i].is_claimed)
Matt Wagantall7a261362011-07-14 19:07:10 -0700596 footswitches[i].rdev->desc->ops->
597 disable(footswitches[i].rdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 mutex_unlock(&claim_lock);
599
600 return 0;
601}
602late_initcall(late_footswitch_init);
603
604static int __init footswitch_init(void)
605{
606 return platform_driver_register(&footswitch_driver);
607}
608subsys_initcall(footswitch_init);
609
610static void __exit footswitch_exit(void)
611{
612 platform_driver_unregister(&footswitch_driver);
613}
614module_exit(footswitch_exit);
615
616MODULE_LICENSE("GPL v2");
617MODULE_DESCRIPTION("MSM8x60 rail footswitch");
618MODULE_ALIAS("platform:footswitch-msm8x60");