blob: a7b26c12d61deba84c0e5507e9984502cb5e215d [file] [log] [blame]
Matt Wagantall1a7ee892012-01-17 18:56:28 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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
Matt Wagantallb82a5132011-12-12 22:26:41 -080013#define pr_fmt(fmt) "%s: " fmt, __func__
14
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/kernel.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070016#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <linux/io.h>
18#include <linux/delay.h>
19#include <linux/platform_device.h>
20#include <linux/err.h>
21#include <linux/regulator/driver.h>
22#include <linux/regulator/machine.h>
23#include <linux/clk.h>
24#include <mach/msm_iomap.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#include <mach/msm_bus.h>
26#include <mach/scm-io.h>
Matt Wagantall33d01f52012-02-23 23:27:44 -080027#include <mach/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include "footswitch.h"
29
30#ifdef CONFIG_MSM_SECURE_IO
31#undef readl_relaxed
32#undef writel_relaxed
33#define readl_relaxed secure_readl
34#define writel_relaxed secure_writel
35#endif
36
37#define REG(off) (MSM_MMSS_CLK_CTL_BASE + (off))
38#define GEMINI_GFS_CTL_REG REG(0x01A0)
39#define GFX2D0_GFS_CTL_REG REG(0x0180)
40#define GFX2D1_GFS_CTL_REG REG(0x0184)
41#define GFX3D_GFS_CTL_REG REG(0x0188)
42#define MDP_GFS_CTL_REG REG(0x0190)
43#define ROT_GFS_CTL_REG REG(0x018C)
44#define VED_GFS_CTL_REG REG(0x0194)
45#define VFE_GFS_CTL_REG REG(0x0198)
46#define VPE_GFS_CTL_REG REG(0x019C)
Matt Wagantall37f34b32011-08-23 18:14:47 -070047#define VCAP_GFS_CTL_REG REG(0x0254)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048
49#define CLAMP_BIT BIT(5)
50#define ENABLE_BIT BIT(8)
51#define RETENTION_BIT BIT(9)
52
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070053#define GFS_DELAY_CNT 31
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055#define RESET_DELAY_US 1
Matt Wagantallb82a5132011-12-12 22:26:41 -080056/* Clock rate to use if one has not previously been set. */
57#define DEFAULT_RATE 27000000
58#define MAX_CLKS 10
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059
60/*
61 * Lock is only needed to protect against the first footswitch_enable()
62 * call occuring concurrently with late_footswitch_init().
63 */
64static DEFINE_MUTEX(claim_lock);
65
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066struct footswitch {
67 struct regulator_dev *rdev;
68 struct regulator_desc desc;
69 void *gfs_ctl_reg;
Matt Wagantallb82a5132011-12-12 22:26:41 -080070 int bus_port0, bus_port1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071 bool is_enabled;
72 bool is_claimed;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070073 struct fs_clk_data *clk_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074 struct clk *core_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075};
76
77static int setup_clocks(struct footswitch *fs)
78{
79 int rc = 0;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070080 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -080081 long rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082
83 /*
Matt Wagantallb82a5132011-12-12 22:26:41 -080084 * Enable all clocks in the power domain. If a specific clock rate is
85 * required for reset timing, set that rate before enabling the clocks.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086 */
Matt Wagantallb82a5132011-12-12 22:26:41 -080087 for (clock = fs->clk_data; clock->clk; clock++) {
88 clock->rate = clk_get_rate(clock->clk);
89 if (!clock->rate || clock->reset_rate) {
90 rate = clock->reset_rate ?
91 clock->reset_rate : DEFAULT_RATE;
92 rc = clk_set_rate(clock->clk, rate);
93 if (rc && rc != -ENOSYS) {
Matt Wagantall27fa2822012-02-22 18:43:27 -080094 pr_err("Failed to set %s %s rate to %lu Hz.\n",
95 fs->desc.name, clock->name, clock->rate);
Matt Wagantallb82a5132011-12-12 22:26:41 -080096 for (clock--; clock >= fs->clk_data; clock--) {
97 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -080098 clk_disable_unprepare(
99 clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800100 clk_set_rate(clock->clk, clock->rate);
101 }
102 return rc;
103 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800105 /*
106 * Some clocks are for reset purposes only. These clocks will
107 * fail to enable. Ignore the failures but keep track of them so
108 * we don't try to disable them later and crash due to
109 * unbalanced calls.
110 */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800111 clock->enabled = !clk_prepare_enable(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Matt Wagantallb82a5132011-12-12 22:26:41 -0800114 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115}
116
117static void restore_clocks(struct footswitch *fs)
118{
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700119 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -0800120
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 /* Restore clocks to their orignal states before setup_clocks(). */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800122 for (clock = fs->clk_data; clock->clk; clock++) {
123 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800124 clk_disable_unprepare(clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800125 if (clock->rate && clk_set_rate(clock->clk, clock->rate))
Matt Wagantall27fa2822012-02-22 18:43:27 -0800126 pr_err("Failed to restore %s %s rate to %lu Hz.\n",
127 fs->desc.name, clock->name, clock->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 }
129}
130
131static int footswitch_is_enabled(struct regulator_dev *rdev)
132{
133 struct footswitch *fs = rdev_get_drvdata(rdev);
134
135 return fs->is_enabled;
136}
137
138static int footswitch_enable(struct regulator_dev *rdev)
139{
140 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700141 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 uint32_t regval, rc = 0;
143
144 mutex_lock(&claim_lock);
145 fs->is_claimed = true;
146 mutex_unlock(&claim_lock);
147
Matt Wagantall88edea92011-07-21 10:29:56 -0700148 /* Return early if already enabled. */
149 regval = readl_relaxed(fs->gfs_ctl_reg);
150 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
151 return 0;
152
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 /* Make sure required clocks are on at the correct rates. */
154 rc = setup_clocks(fs);
155 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800156 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157
158 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800159 if (fs->bus_port0) {
160 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800162 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800163 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 }
165 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800166 if (fs->bus_port1) {
167 rc = msm_bus_axi_portunhalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800169 pr_err("%s port 1 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800170 goto err_port2_halt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 }
172 }
173
174 /*
175 * (Re-)Assert resets for all clocks in the clock domain, since
176 * footswitch_enable() is first called before footswitch_disable()
177 * and resets should be asserted before power is restored.
178 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800179 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800180 ; /* Do nothing */
181 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800182 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 /* Wait for synchronous resets to propagate. */
184 udelay(RESET_DELAY_US);
185
186 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 regval |= ENABLE_BIT;
188 writel_relaxed(regval, fs->gfs_ctl_reg);
189 /* Wait for the rail to fully charge. */
190 mb();
191 udelay(1);
192
193 /* Un-clamp the I/O ports. */
194 regval &= ~CLAMP_BIT;
195 writel_relaxed(regval, fs->gfs_ctl_reg);
196
197 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800198 for (clock = fs->clk_data; clock->clk; clock++)
199 clk_reset(clock->clk, CLK_RESET_DEASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 /* 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
Matt Wagantall8bec3662012-01-25 11:06:13 -0800208 /* Prevent core memory from collapsing when its clock is gated. */
209 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN);
210
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 /* Return clocks to their state before this function. */
212 restore_clocks(fs);
213
214 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800215 return 0;
216
217err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800218 msm_bus_axi_porthalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800219err:
220 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 return rc;
222}
223
224static int footswitch_disable(struct regulator_dev *rdev)
225{
226 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700227 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 uint32_t regval, rc = 0;
229
Matt Wagantall88edea92011-07-21 10:29:56 -0700230 /* Return early if already disabled. */
231 regval = readl_relaxed(fs->gfs_ctl_reg);
232 if ((regval & ENABLE_BIT) == 0)
233 return 0;
234
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 /* Make sure required clocks are on at the correct rates. */
236 rc = setup_clocks(fs);
237 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800238 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239
Matt Wagantall8bec3662012-01-25 11:06:13 -0800240 /* Allow core memory to collapse when its clock is gated. */
241 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN);
242
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800244 if (fs->bus_port0) {
245 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800247 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800248 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 }
250 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800251 if (fs->bus_port1) {
252 rc = msm_bus_axi_porthalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800254 pr_err("%s port 1 halt failed.\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 goto err_port2_halt;
256 }
257 }
258
259 /*
260 * Assert resets for all clocks in the clock domain so that
261 * outputs settle prior to clamping.
262 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800263 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800264 ; /* Do nothing */
265 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800266 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 /* Wait for synchronous resets to propagate. */
268 udelay(RESET_DELAY_US);
269
270 /*
Matt Wagantall1ab7d942011-12-02 17:59:57 -0800271 * Return clocks to their state before this function. For robustness
272 * if memory-retention across collapses is required, clocks should
273 * be disabled before asserting the clamps. Assuming clocks were off
274 * before entering footswitch_disable(), this will be true.
275 */
276 restore_clocks(fs);
277
278 /*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 * Clamp the I/O ports of the core to ensure the values
280 * remain fixed while the core is collapsed.
281 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 regval |= CLAMP_BIT;
283 writel_relaxed(regval, fs->gfs_ctl_reg);
284
285 /* Collapse the power rail at the footswitch. */
286 regval &= ~ENABLE_BIT;
287 writel_relaxed(regval, fs->gfs_ctl_reg);
288
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800290 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
292err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800293 msm_bus_axi_portunhalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800294err:
Matt Wagantall8bec3662012-01-25 11:06:13 -0800295 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800296 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 return rc;
298}
299
300static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
301{
302 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700303 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 uint32_t regval, rc = 0;
305
306 mutex_lock(&claim_lock);
307 fs->is_claimed = true;
308 mutex_unlock(&claim_lock);
309
Matt Wagantall88edea92011-07-21 10:29:56 -0700310 /* Return early if already enabled. */
311 regval = readl_relaxed(fs->gfs_ctl_reg);
312 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
313 return 0;
314
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 /* Make sure required clocks are on at the correct rates. */
316 rc = setup_clocks(fs);
317 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800318 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319
320 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800321 if (fs->bus_port0) {
322 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800324 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800325 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 }
327 }
328
329 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800330 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331
332 /*
333 * (Re-)Assert resets for all clocks in the clock domain, since
334 * footswitch_enable() is first called before footswitch_disable()
335 * and resets should be asserted before power is restored.
336 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800337 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800338 ; /* Do nothing */
339 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800340 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341 /* Wait for synchronous resets to propagate. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800342 udelay(RESET_DELAY_US);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343
344 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 regval |= ENABLE_BIT;
346 writel_relaxed(regval, fs->gfs_ctl_reg);
347 mb();
348 udelay(1);
349
350 /* Un-clamp the I/O ports. */
351 regval &= ~CLAMP_BIT;
352 writel_relaxed(regval, fs->gfs_ctl_reg);
353
354 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800355 for (clock = fs->clk_data; clock->clk; clock++)
356 clk_reset(clock->clk, CLK_RESET_DEASSERT);
357 udelay(RESET_DELAY_US);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358
359 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800360 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361
Matt Wagantall8bec3662012-01-25 11:06:13 -0800362 /* Prevent core memory from collapsing when its clock is gated. */
363 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN);
364
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 /* Return clocks to their state before this function. */
366 restore_clocks(fs);
367
368 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800369 return 0;
370
371err:
372 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 return rc;
374}
375
376static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
377{
378 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700379 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 uint32_t regval, rc = 0;
381
Matt Wagantall88edea92011-07-21 10:29:56 -0700382 /* Return early if already disabled. */
383 regval = readl_relaxed(fs->gfs_ctl_reg);
384 if ((regval & ENABLE_BIT) == 0)
385 return 0;
386
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 /* Make sure required clocks are on at the correct rates. */
388 rc = setup_clocks(fs);
389 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800390 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391
Matt Wagantall8bec3662012-01-25 11:06:13 -0800392 /* Allow core memory to collapse when its clock is gated. */
393 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN);
394
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800396 if (fs->bus_port0) {
397 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800399 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800400 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 }
402 }
403
404 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800405 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406
407 /*
408 * Assert resets for all clocks in the clock domain so that
409 * outputs settle prior to clamping.
410 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800411 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800412 ; /* Do nothing */
413 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800414 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415 /* Wait for synchronous resets to propagate. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800416 udelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417
418 /*
419 * Clamp the I/O ports of the core to ensure the values
420 * remain fixed while the core is collapsed.
421 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 regval |= CLAMP_BIT;
423 writel_relaxed(regval, fs->gfs_ctl_reg);
424
425 /* Collapse the power rail at the footswitch. */
426 regval &= ~ENABLE_BIT;
427 writel_relaxed(regval, fs->gfs_ctl_reg);
428
429 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800430 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
432 /* Return clocks to their state before this function. */
433 restore_clocks(fs);
434
435 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800436 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800438err:
Matt Wagantall8bec3662012-01-25 11:06:13 -0800439 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800440 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441 return rc;
442}
443
444static struct regulator_ops standard_fs_ops = {
445 .is_enabled = footswitch_is_enabled,
446 .enable = footswitch_enable,
447 .disable = footswitch_disable,
448};
449
450static struct regulator_ops gfx2d_fs_ops = {
451 .is_enabled = footswitch_is_enabled,
452 .enable = gfx2d_footswitch_enable,
453 .disable = gfx2d_footswitch_disable,
454};
455
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700456#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 [(_id)] = { \
458 .desc = { \
459 .id = (_id), \
460 .name = (_name), \
461 .ops = (_ops), \
462 .type = REGULATOR_VOLTAGE, \
463 .owner = THIS_MODULE, \
464 }, \
465 .gfs_ctl_reg = (_gfs_ctl_reg), \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466 }
467static struct footswitch footswitches[] = {
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700468 FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG),
469 FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG),
470 FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG),
471 FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG),
472 FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops, MDP_GFS_CTL_REG),
473 FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops, ROT_GFS_CTL_REG),
474 FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops, VED_GFS_CTL_REG),
475 FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops, VFE_GFS_CTL_REG),
476 FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops, VPE_GFS_CTL_REG),
477 FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops, VCAP_GFS_CTL_REG),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478};
479
480static int footswitch_probe(struct platform_device *pdev)
481{
482 struct footswitch *fs;
483 struct regulator_init_data *init_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700484 struct fs_driver_data *driver_data;
485 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 uint32_t regval, rc = 0;
487
488 if (pdev == NULL)
489 return -EINVAL;
490
491 if (pdev->id >= MAX_FS)
492 return -ENODEV;
493
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494 init_data = pdev->dev.platform_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700495 driver_data = init_data->driver_data;
496 fs = &footswitches[pdev->id];
497 fs->clk_data = driver_data->clks;
498 fs->bus_port0 = driver_data->bus_port0;
499 fs->bus_port1 = driver_data->bus_port1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500
Matt Wagantallb82a5132011-12-12 22:26:41 -0800501 for (clock = fs->clk_data; clock->name; clock++) {
502 clock->clk = clk_get(&pdev->dev, clock->name);
503 if (IS_ERR(clock->clk)) {
504 rc = PTR_ERR(clock->clk);
Matt Wagantall27fa2822012-02-22 18:43:27 -0800505 pr_err("%s clk_get(%s) failed\n", fs->desc.name,
506 clock->name);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800507 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800509 if (!strncmp(clock->name, "core_clk", 8))
510 fs->core_clk = clock->clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511 }
512
513 /*
514 * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all
515 * after enabling the footswitch. Also ensure the retention bit is
516 * clear so disabling the footswitch will power-collapse the core.
517 */
518 regval = readl_relaxed(fs->gfs_ctl_reg);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700519 regval |= GFS_DELAY_CNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 regval &= ~RETENTION_BIT;
521 writel_relaxed(regval, fs->gfs_ctl_reg);
522
Rajendra Nayak11eafc62011-11-18 16:47:19 +0530523 fs->rdev = regulator_register(&fs->desc, &pdev->dev,
524 init_data, fs, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 if (IS_ERR(footswitches[pdev->id].rdev)) {
Matt Wagantallb82a5132011-12-12 22:26:41 -0800526 pr_err("regulator_register(\"%s\") failed\n",
527 fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528 rc = PTR_ERR(footswitches[pdev->id].rdev);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800529 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 }
531
532 return 0;
533
Matt Wagantallb82a5132011-12-12 22:26:41 -0800534err:
535 for (clock = fs->clk_data; clock->clk; clock++)
536 clk_put(clock->clk);
537
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 return rc;
539}
540
541static int __devexit footswitch_remove(struct platform_device *pdev)
542{
543 struct footswitch *fs = &footswitches[pdev->id];
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700544 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545
Matt Wagantallb82a5132011-12-12 22:26:41 -0800546 for (clock = fs->clk_data; clock->clk; clock++)
547 clk_put(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 regulator_unregister(fs->rdev);
549
550 return 0;
551}
552
553static struct platform_driver footswitch_driver = {
554 .probe = footswitch_probe,
555 .remove = __devexit_p(footswitch_remove),
556 .driver = {
Matt Wagantall49722712011-08-17 18:50:53 -0700557 .name = "footswitch-8x60",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558 .owner = THIS_MODULE,
559 },
560};
561
562static int __init late_footswitch_init(void)
563{
564 int i;
565
566 mutex_lock(&claim_lock);
567 /* Turn off all registered but unused footswitches. */
568 for (i = 0; i < ARRAY_SIZE(footswitches); i++)
569 if (footswitches[i].rdev && !footswitches[i].is_claimed)
Matt Wagantall7a261362011-07-14 19:07:10 -0700570 footswitches[i].rdev->desc->ops->
571 disable(footswitches[i].rdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 mutex_unlock(&claim_lock);
573
574 return 0;
575}
576late_initcall(late_footswitch_init);
577
578static int __init footswitch_init(void)
579{
580 return platform_driver_register(&footswitch_driver);
581}
582subsys_initcall(footswitch_init);
583
584static void __exit footswitch_exit(void)
585{
586 platform_driver_unregister(&footswitch_driver);
587}
588module_exit(footswitch_exit);
589
590MODULE_LICENSE("GPL v2");
591MODULE_DESCRIPTION("MSM8x60 rail footswitch");
592MODULE_ALIAS("platform:footswitch-msm8x60");