blob: 581c563b86d79f5278d4886b26a018c52f7f06e6 [file] [log] [blame]
Matt Wagantallcd6f4652013-04-01 15:22:47 -07001/* Copyright (c) 2010-2013, The Linux Foundation. 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
Matt Wagantallcd6f4652013-04-01 15:22:47 -070055#define DEFAULT_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;
Matt Wagantallcd6f4652013-04-01 15:22:47 -070075 unsigned long reset_delay_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076};
77
78static int setup_clocks(struct footswitch *fs)
79{
80 int rc = 0;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070081 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -080082 long rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083
84 /*
Matt Wagantallb82a5132011-12-12 22:26:41 -080085 * Enable all clocks in the power domain. If a specific clock rate is
86 * required for reset timing, set that rate before enabling the clocks.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 */
Matt Wagantallb82a5132011-12-12 22:26:41 -080088 for (clock = fs->clk_data; clock->clk; clock++) {
89 clock->rate = clk_get_rate(clock->clk);
90 if (!clock->rate || clock->reset_rate) {
91 rate = clock->reset_rate ?
92 clock->reset_rate : DEFAULT_RATE;
93 rc = clk_set_rate(clock->clk, rate);
94 if (rc && rc != -ENOSYS) {
Matt Wagantall27fa2822012-02-22 18:43:27 -080095 pr_err("Failed to set %s %s rate to %lu Hz.\n",
96 fs->desc.name, clock->name, clock->rate);
Matt Wagantallb82a5132011-12-12 22:26:41 -080097 for (clock--; clock >= fs->clk_data; clock--) {
98 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -080099 clk_disable_unprepare(
100 clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800101 clk_set_rate(clock->clk, clock->rate);
102 }
103 return rc;
104 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800106 /*
107 * Some clocks are for reset purposes only. These clocks will
108 * fail to enable. Ignore the failures but keep track of them so
109 * we don't try to disable them later and crash due to
110 * unbalanced calls.
111 */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800112 clock->enabled = !clk_prepare_enable(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114
Matt Wagantallb82a5132011-12-12 22:26:41 -0800115 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116}
117
118static void restore_clocks(struct footswitch *fs)
119{
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700120 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -0800121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 /* Restore clocks to their orignal states before setup_clocks(). */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800123 for (clock = fs->clk_data; clock->clk; clock++) {
124 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800125 clk_disable_unprepare(clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800126 if (clock->rate && clk_set_rate(clock->clk, clock->rate))
Matt Wagantall27fa2822012-02-22 18:43:27 -0800127 pr_err("Failed to restore %s %s rate to %lu Hz.\n",
128 fs->desc.name, clock->name, clock->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129 }
130}
131
132static int footswitch_is_enabled(struct regulator_dev *rdev)
133{
134 struct footswitch *fs = rdev_get_drvdata(rdev);
135
136 return fs->is_enabled;
137}
138
139static int footswitch_enable(struct regulator_dev *rdev)
140{
141 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700142 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 uint32_t regval, rc = 0;
144
145 mutex_lock(&claim_lock);
146 fs->is_claimed = true;
147 mutex_unlock(&claim_lock);
148
Matt Wagantall88edea92011-07-21 10:29:56 -0700149 /* Return early if already enabled. */
150 regval = readl_relaxed(fs->gfs_ctl_reg);
151 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
152 return 0;
153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 /* Make sure required clocks are on at the correct rates. */
155 rc = setup_clocks(fs);
156 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800157 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158
159 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800160 if (fs->bus_port0) {
161 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800163 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800164 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 }
166 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800167 if (fs->bus_port1) {
168 rc = msm_bus_axi_portunhalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800170 pr_err("%s port 1 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800171 goto err_port2_halt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 }
173 }
174
175 /*
176 * (Re-)Assert resets for all clocks in the clock domain, since
177 * footswitch_enable() is first called before footswitch_disable()
178 * and resets should be asserted before power is restored.
179 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800180 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800181 ; /* Do nothing */
182 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800183 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700185 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186
187 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188 regval |= ENABLE_BIT;
189 writel_relaxed(regval, fs->gfs_ctl_reg);
190 /* Wait for the rail to fully charge. */
191 mb();
192 udelay(1);
193
194 /* Un-clamp the I/O ports. */
195 regval &= ~CLAMP_BIT;
196 writel_relaxed(regval, fs->gfs_ctl_reg);
197
198 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800199 for (clock = fs->clk_data; clock->clk; clock++)
200 clk_reset(clock->clk, CLK_RESET_DEASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 /* Toggle core reset again after first power-on (required for GFX3D). */
202 if (fs->desc.id == FS_GFX3D) {
203 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700204 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700206 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 }
208
Matt Wagantall8bec3662012-01-25 11:06:13 -0800209 /* Prevent core memory from collapsing when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700210 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800211
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 /* Return clocks to their state before this function. */
213 restore_clocks(fs);
214
215 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800216 return 0;
217
218err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800219 msm_bus_axi_porthalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800220err:
221 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 return rc;
223}
224
225static int footswitch_disable(struct regulator_dev *rdev)
226{
227 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700228 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 uint32_t regval, rc = 0;
230
Matt Wagantall88edea92011-07-21 10:29:56 -0700231 /* Return early if already disabled. */
232 regval = readl_relaxed(fs->gfs_ctl_reg);
233 if ((regval & ENABLE_BIT) == 0)
234 return 0;
235
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236 /* Make sure required clocks are on at the correct rates. */
237 rc = setup_clocks(fs);
238 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800239 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240
Matt Wagantall8bec3662012-01-25 11:06:13 -0800241 /* Allow core memory to collapse when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700242 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800243
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800245 if (fs->bus_port0) {
246 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800248 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800249 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 }
251 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800252 if (fs->bus_port1) {
253 rc = msm_bus_axi_porthalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800255 pr_err("%s port 1 halt failed.\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 goto err_port2_halt;
257 }
258 }
259
260 /*
261 * Assert resets for all clocks in the clock domain so that
262 * outputs settle prior to clamping.
263 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800264 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800265 ; /* Do nothing */
266 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800267 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700269 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270
271 /*
Matt Wagantall1ab7d942011-12-02 17:59:57 -0800272 * Return clocks to their state before this function. For robustness
273 * if memory-retention across collapses is required, clocks should
274 * be disabled before asserting the clamps. Assuming clocks were off
275 * before entering footswitch_disable(), this will be true.
276 */
277 restore_clocks(fs);
278
279 /*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 * Clamp the I/O ports of the core to ensure the values
281 * remain fixed while the core is collapsed.
282 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 regval |= CLAMP_BIT;
284 writel_relaxed(regval, fs->gfs_ctl_reg);
285
286 /* Collapse the power rail at the footswitch. */
287 regval &= ~ENABLE_BIT;
288 writel_relaxed(regval, fs->gfs_ctl_reg);
289
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800291 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292
293err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800294 msm_bus_axi_portunhalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800295err:
Matt Wagantall76377792013-04-10 19:37:58 -0700296 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800297 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 return rc;
299}
300
301static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
302{
303 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700304 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 uint32_t regval, rc = 0;
306
307 mutex_lock(&claim_lock);
308 fs->is_claimed = true;
309 mutex_unlock(&claim_lock);
310
Matt Wagantall88edea92011-07-21 10:29:56 -0700311 /* Return early if already enabled. */
312 regval = readl_relaxed(fs->gfs_ctl_reg);
313 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
314 return 0;
315
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 /* Make sure required clocks are on at the correct rates. */
317 rc = setup_clocks(fs);
318 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800319 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320
321 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800322 if (fs->bus_port0) {
323 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800325 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800326 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327 }
328 }
329
330 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800331 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332
333 /*
334 * (Re-)Assert resets for all clocks in the clock domain, since
335 * footswitch_enable() is first called before footswitch_disable()
336 * and resets should be asserted before power is restored.
337 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800338 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800339 ; /* Do nothing */
340 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800341 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700343 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344
345 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 regval |= ENABLE_BIT;
347 writel_relaxed(regval, fs->gfs_ctl_reg);
348 mb();
349 udelay(1);
350
351 /* Un-clamp the I/O ports. */
352 regval &= ~CLAMP_BIT;
353 writel_relaxed(regval, fs->gfs_ctl_reg);
354
355 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800356 for (clock = fs->clk_data; clock->clk; clock++)
357 clk_reset(clock->clk, CLK_RESET_DEASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700358 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
360 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800361 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362
Matt Wagantall8bec3662012-01-25 11:06:13 -0800363 /* Prevent core memory from collapsing when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700364 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800365
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366 /* Return clocks to their state before this function. */
367 restore_clocks(fs);
368
369 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800370 return 0;
371
372err:
373 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374 return rc;
375}
376
377static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
378{
379 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700380 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 uint32_t regval, rc = 0;
382
Matt Wagantall88edea92011-07-21 10:29:56 -0700383 /* Return early if already disabled. */
384 regval = readl_relaxed(fs->gfs_ctl_reg);
385 if ((regval & ENABLE_BIT) == 0)
386 return 0;
387
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 /* Make sure required clocks are on at the correct rates. */
389 rc = setup_clocks(fs);
390 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800391 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Matt Wagantall8bec3662012-01-25 11:06:13 -0800393 /* Allow core memory to collapse when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700394 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800395
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800397 if (fs->bus_port0) {
398 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800400 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800401 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 }
403 }
404
405 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800406 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
408 /*
409 * Assert resets for all clocks in the clock domain so that
410 * outputs settle prior to clamping.
411 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800412 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800413 ; /* Do nothing */
414 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800415 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 /* Wait for synchronous resets to propagate. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800417 udelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418
419 /*
420 * Clamp the I/O ports of the core to ensure the values
421 * remain fixed while the core is collapsed.
422 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423 regval |= CLAMP_BIT;
424 writel_relaxed(regval, fs->gfs_ctl_reg);
425
426 /* Collapse the power rail at the footswitch. */
427 regval &= ~ENABLE_BIT;
428 writel_relaxed(regval, fs->gfs_ctl_reg);
429
430 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800431 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432
433 /* Return clocks to their state before this function. */
434 restore_clocks(fs);
435
436 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800437 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800439err:
Matt Wagantall76377792013-04-10 19:37:58 -0700440 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800441 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 return rc;
443}
444
445static struct regulator_ops standard_fs_ops = {
446 .is_enabled = footswitch_is_enabled,
447 .enable = footswitch_enable,
448 .disable = footswitch_disable,
449};
450
451static struct regulator_ops gfx2d_fs_ops = {
452 .is_enabled = footswitch_is_enabled,
453 .enable = gfx2d_footswitch_enable,
454 .disable = gfx2d_footswitch_disable,
455};
456
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700457#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 [(_id)] = { \
459 .desc = { \
460 .id = (_id), \
461 .name = (_name), \
462 .ops = (_ops), \
463 .type = REGULATOR_VOLTAGE, \
464 .owner = THIS_MODULE, \
465 }, \
466 .gfs_ctl_reg = (_gfs_ctl_reg), \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 }
468static struct footswitch footswitches[] = {
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700469 FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG),
470 FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG),
471 FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG),
472 FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG),
473 FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops, MDP_GFS_CTL_REG),
474 FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops, ROT_GFS_CTL_REG),
475 FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops, VED_GFS_CTL_REG),
476 FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops, VFE_GFS_CTL_REG),
477 FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops, VPE_GFS_CTL_REG),
478 FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops, VCAP_GFS_CTL_REG),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479};
480
481static int footswitch_probe(struct platform_device *pdev)
482{
483 struct footswitch *fs;
484 struct regulator_init_data *init_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700485 struct fs_driver_data *driver_data;
486 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 uint32_t regval, rc = 0;
488
489 if (pdev == NULL)
490 return -EINVAL;
491
492 if (pdev->id >= MAX_FS)
493 return -ENODEV;
494
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495 init_data = pdev->dev.platform_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700496 driver_data = init_data->driver_data;
497 fs = &footswitches[pdev->id];
498 fs->clk_data = driver_data->clks;
499 fs->bus_port0 = driver_data->bus_port0;
500 fs->bus_port1 = driver_data->bus_port1;
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700501 fs->reset_delay_us =
502 driver_data->reset_delay_us ? : DEFAULT_RESET_DELAY_US;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503
Matt Wagantallb82a5132011-12-12 22:26:41 -0800504 for (clock = fs->clk_data; clock->name; clock++) {
505 clock->clk = clk_get(&pdev->dev, clock->name);
506 if (IS_ERR(clock->clk)) {
507 rc = PTR_ERR(clock->clk);
Matt Wagantall27fa2822012-02-22 18:43:27 -0800508 pr_err("%s clk_get(%s) failed\n", fs->desc.name,
509 clock->name);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800510 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800512 if (!strncmp(clock->name, "core_clk", 8))
513 fs->core_clk = clock->clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514 }
515
516 /*
517 * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all
518 * after enabling the footswitch. Also ensure the retention bit is
519 * clear so disabling the footswitch will power-collapse the core.
520 */
521 regval = readl_relaxed(fs->gfs_ctl_reg);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700522 regval |= GFS_DELAY_CNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 regval &= ~RETENTION_BIT;
524 writel_relaxed(regval, fs->gfs_ctl_reg);
525
Rajendra Nayak11eafc62011-11-18 16:47:19 +0530526 fs->rdev = regulator_register(&fs->desc, &pdev->dev,
527 init_data, fs, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528 if (IS_ERR(footswitches[pdev->id].rdev)) {
Matt Wagantallb82a5132011-12-12 22:26:41 -0800529 pr_err("regulator_register(\"%s\") failed\n",
530 fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531 rc = PTR_ERR(footswitches[pdev->id].rdev);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800532 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533 }
534
535 return 0;
536
Matt Wagantallb82a5132011-12-12 22:26:41 -0800537err:
538 for (clock = fs->clk_data; clock->clk; clock++)
539 clk_put(clock->clk);
540
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 return rc;
542}
543
544static int __devexit footswitch_remove(struct platform_device *pdev)
545{
546 struct footswitch *fs = &footswitches[pdev->id];
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700547 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548
Matt Wagantallb82a5132011-12-12 22:26:41 -0800549 for (clock = fs->clk_data; clock->clk; clock++)
550 clk_put(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 regulator_unregister(fs->rdev);
552
553 return 0;
554}
555
556static struct platform_driver footswitch_driver = {
557 .probe = footswitch_probe,
558 .remove = __devexit_p(footswitch_remove),
559 .driver = {
Matt Wagantall49722712011-08-17 18:50:53 -0700560 .name = "footswitch-8x60",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561 .owner = THIS_MODULE,
562 },
563};
564
565static int __init late_footswitch_init(void)
566{
567 int i;
568
569 mutex_lock(&claim_lock);
570 /* Turn off all registered but unused footswitches. */
571 for (i = 0; i < ARRAY_SIZE(footswitches); i++)
572 if (footswitches[i].rdev && !footswitches[i].is_claimed)
Matt Wagantall7a261362011-07-14 19:07:10 -0700573 footswitches[i].rdev->desc->ops->
574 disable(footswitches[i].rdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 mutex_unlock(&claim_lock);
576
577 return 0;
578}
579late_initcall(late_footswitch_init);
580
581static int __init footswitch_init(void)
582{
583 return platform_driver_register(&footswitch_driver);
584}
585subsys_initcall(footswitch_init);
586
587static void __exit footswitch_exit(void)
588{
589 platform_driver_unregister(&footswitch_driver);
590}
591module_exit(footswitch_exit);
592
593MODULE_LICENSE("GPL v2");
594MODULE_DESCRIPTION("MSM8x60 rail footswitch");
595MODULE_ALIAS("platform:footswitch-msm8x60");