blob: 67bbd5ee457e5eef78e02d21b32f5734a1f29dae [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>
Matt Wagantall6a118582013-04-01 16:18:37 -070028#include <mach/rpm.h>
29
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include "footswitch.h"
Matt Wagantall6a118582013-04-01 16:18:37 -070031#include "rpm_resources.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33#ifdef CONFIG_MSM_SECURE_IO
34#undef readl_relaxed
35#undef writel_relaxed
36#define readl_relaxed secure_readl
37#define writel_relaxed secure_writel
38#endif
39
40#define REG(off) (MSM_MMSS_CLK_CTL_BASE + (off))
41#define GEMINI_GFS_CTL_REG REG(0x01A0)
42#define GFX2D0_GFS_CTL_REG REG(0x0180)
43#define GFX2D1_GFS_CTL_REG REG(0x0184)
44#define GFX3D_GFS_CTL_REG REG(0x0188)
45#define MDP_GFS_CTL_REG REG(0x0190)
46#define ROT_GFS_CTL_REG REG(0x018C)
47#define VED_GFS_CTL_REG REG(0x0194)
48#define VFE_GFS_CTL_REG REG(0x0198)
49#define VPE_GFS_CTL_REG REG(0x019C)
Matt Wagantall37f34b32011-08-23 18:14:47 -070050#define VCAP_GFS_CTL_REG REG(0x0254)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
52#define CLAMP_BIT BIT(5)
53#define ENABLE_BIT BIT(8)
54#define RETENTION_BIT BIT(9)
55
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070056#define GFS_DELAY_CNT 31
57
Matt Wagantallcd6f4652013-04-01 15:22:47 -070058#define DEFAULT_RESET_DELAY_US 1
Matt Wagantallb82a5132011-12-12 22:26:41 -080059/* Clock rate to use if one has not previously been set. */
60#define DEFAULT_RATE 27000000
61#define MAX_CLKS 10
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062
63/*
64 * Lock is only needed to protect against the first footswitch_enable()
65 * call occuring concurrently with late_footswitch_init().
66 */
67static DEFINE_MUTEX(claim_lock);
68
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069struct footswitch {
70 struct regulator_dev *rdev;
71 struct regulator_desc desc;
72 void *gfs_ctl_reg;
Matt Wagantallb82a5132011-12-12 22:26:41 -080073 int bus_port0, bus_port1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074 bool is_enabled;
75 bool is_claimed;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070076 struct fs_clk_data *clk_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077 struct clk *core_clk;
Matt Wagantallcd6f4652013-04-01 15:22:47 -070078 unsigned long reset_delay_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079};
80
81static int setup_clocks(struct footswitch *fs)
82{
83 int rc = 0;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -070084 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -080085 long rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086
87 /*
Matt Wagantallb82a5132011-12-12 22:26:41 -080088 * Enable all clocks in the power domain. If a specific clock rate is
89 * required for reset timing, set that rate before enabling the clocks.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 */
Matt Wagantallb82a5132011-12-12 22:26:41 -080091 for (clock = fs->clk_data; clock->clk; clock++) {
92 clock->rate = clk_get_rate(clock->clk);
93 if (!clock->rate || clock->reset_rate) {
94 rate = clock->reset_rate ?
95 clock->reset_rate : DEFAULT_RATE;
96 rc = clk_set_rate(clock->clk, rate);
97 if (rc && rc != -ENOSYS) {
Matt Wagantall27fa2822012-02-22 18:43:27 -080098 pr_err("Failed to set %s %s rate to %lu Hz.\n",
99 fs->desc.name, clock->name, clock->rate);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800100 for (clock--; clock >= fs->clk_data; clock--) {
101 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800102 clk_disable_unprepare(
103 clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800104 clk_set_rate(clock->clk, clock->rate);
105 }
106 return rc;
107 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800109 /*
110 * Some clocks are for reset purposes only. These clocks will
111 * fail to enable. Ignore the failures but keep track of them so
112 * we don't try to disable them later and crash due to
113 * unbalanced calls.
114 */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800115 clock->enabled = !clk_prepare_enable(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117
Matt Wagantallb82a5132011-12-12 22:26:41 -0800118 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119}
120
121static void restore_clocks(struct footswitch *fs)
122{
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700123 struct fs_clk_data *clock;
Matt Wagantallb82a5132011-12-12 22:26:41 -0800124
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 /* Restore clocks to their orignal states before setup_clocks(). */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800126 for (clock = fs->clk_data; clock->clk; clock++) {
127 if (clock->enabled)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800128 clk_disable_unprepare(clock->clk);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800129 if (clock->rate && clk_set_rate(clock->clk, clock->rate))
Matt Wagantall27fa2822012-02-22 18:43:27 -0800130 pr_err("Failed to restore %s %s rate to %lu Hz.\n",
131 fs->desc.name, clock->name, clock->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 }
133}
134
135static int footswitch_is_enabled(struct regulator_dev *rdev)
136{
137 struct footswitch *fs = rdev_get_drvdata(rdev);
138
139 return fs->is_enabled;
140}
141
142static int footswitch_enable(struct regulator_dev *rdev)
143{
144 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700145 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 uint32_t regval, rc = 0;
147
148 mutex_lock(&claim_lock);
149 fs->is_claimed = true;
150 mutex_unlock(&claim_lock);
151
Matt Wagantall88edea92011-07-21 10:29:56 -0700152 /* Return early if already enabled. */
153 regval = readl_relaxed(fs->gfs_ctl_reg);
154 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
155 return 0;
156
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 /* Make sure required clocks are on at the correct rates. */
158 rc = setup_clocks(fs);
159 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800160 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161
162 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800163 if (fs->bus_port0) {
164 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800166 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800167 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 }
169 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800170 if (fs->bus_port1) {
171 rc = msm_bus_axi_portunhalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800173 pr_err("%s port 1 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800174 goto err_port2_halt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 }
176 }
177
178 /*
179 * (Re-)Assert resets for all clocks in the clock domain, since
180 * footswitch_enable() is first called before footswitch_disable()
181 * and resets should be asserted before power is restored.
182 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800183 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800184 ; /* Do nothing */
185 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800186 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700188 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
190 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 regval |= ENABLE_BIT;
192 writel_relaxed(regval, fs->gfs_ctl_reg);
193 /* Wait for the rail to fully charge. */
194 mb();
195 udelay(1);
196
197 /* Un-clamp the I/O ports. */
198 regval &= ~CLAMP_BIT;
199 writel_relaxed(regval, fs->gfs_ctl_reg);
200
201 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800202 for (clock = fs->clk_data; clock->clk; clock++)
203 clk_reset(clock->clk, CLK_RESET_DEASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 /* Toggle core reset again after first power-on (required for GFX3D). */
205 if (fs->desc.id == FS_GFX3D) {
206 clk_reset(fs->core_clk, CLK_RESET_ASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700207 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700209 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 }
211
Matt Wagantall8bec3662012-01-25 11:06:13 -0800212 /* Prevent core memory from collapsing when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700213 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800214
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 /* Return clocks to their state before this function. */
216 restore_clocks(fs);
217
218 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800219 return 0;
220
221err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800222 msm_bus_axi_porthalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800223err:
224 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 return rc;
226}
227
228static int footswitch_disable(struct regulator_dev *rdev)
229{
230 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700231 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232 uint32_t regval, rc = 0;
233
Matt Wagantall88edea92011-07-21 10:29:56 -0700234 /* Return early if already disabled. */
235 regval = readl_relaxed(fs->gfs_ctl_reg);
236 if ((regval & ENABLE_BIT) == 0)
237 return 0;
238
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 /* Make sure required clocks are on at the correct rates. */
240 rc = setup_clocks(fs);
241 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800242 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243
Matt Wagantall8bec3662012-01-25 11:06:13 -0800244 /* Allow core memory to collapse when its clock is gated. */
Shashank Mittalc1424842013-04-05 14:14:52 -0700245 if (fs->desc.id != FS_GFX3D_8064)
246 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800247
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800249 if (fs->bus_port0) {
250 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800252 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800253 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 }
255 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800256 if (fs->bus_port1) {
257 rc = msm_bus_axi_porthalt(fs->bus_port1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800259 pr_err("%s port 1 halt failed.\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 goto err_port2_halt;
261 }
262 }
263
264 /*
265 * Assert resets for all clocks in the clock domain so that
266 * outputs settle prior to clamping.
267 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800268 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800269 ; /* Do nothing */
270 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800271 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700273 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274
275 /*
Matt Wagantall1ab7d942011-12-02 17:59:57 -0800276 * Return clocks to their state before this function. For robustness
277 * if memory-retention across collapses is required, clocks should
278 * be disabled before asserting the clamps. Assuming clocks were off
279 * before entering footswitch_disable(), this will be true.
280 */
281 restore_clocks(fs);
282
283 /*
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 * Clamp the I/O ports of the core to ensure the values
285 * remain fixed while the core is collapsed.
286 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 regval |= CLAMP_BIT;
288 writel_relaxed(regval, fs->gfs_ctl_reg);
289
290 /* Collapse the power rail at the footswitch. */
291 regval &= ~ENABLE_BIT;
292 writel_relaxed(regval, fs->gfs_ctl_reg);
293
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800295 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296
297err_port2_halt:
Matt Wagantallb82a5132011-12-12 22:26:41 -0800298 msm_bus_axi_portunhalt(fs->bus_port0);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800299err:
Matt Wagantall76377792013-04-10 19:37:58 -0700300 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800301 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302 return rc;
303}
304
305static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
306{
307 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700308 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 uint32_t regval, rc = 0;
310
311 mutex_lock(&claim_lock);
312 fs->is_claimed = true;
313 mutex_unlock(&claim_lock);
314
Matt Wagantall88edea92011-07-21 10:29:56 -0700315 /* Return early if already enabled. */
316 regval = readl_relaxed(fs->gfs_ctl_reg);
317 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
318 return 0;
319
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 /* Make sure required clocks are on at the correct rates. */
321 rc = setup_clocks(fs);
322 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800323 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324
325 /* Un-halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800326 if (fs->bus_port0) {
327 rc = msm_bus_axi_portunhalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800329 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800330 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 }
332 }
333
334 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800335 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336
337 /*
338 * (Re-)Assert resets for all clocks in the clock domain, since
339 * footswitch_enable() is first called before footswitch_disable()
340 * and resets should be asserted before power is restored.
341 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800342 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800343 ; /* Do nothing */
344 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800345 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 /* Wait for synchronous resets to propagate. */
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700347 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348
349 /* Enable the power rail at the footswitch. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350 regval |= ENABLE_BIT;
351 writel_relaxed(regval, fs->gfs_ctl_reg);
352 mb();
353 udelay(1);
354
355 /* Un-clamp the I/O ports. */
356 regval &= ~CLAMP_BIT;
357 writel_relaxed(regval, fs->gfs_ctl_reg);
358
359 /* Deassert resets for all clocks in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800360 for (clock = fs->clk_data; clock->clk; clock++)
361 clk_reset(clock->clk, CLK_RESET_DEASSERT);
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700362 udelay(fs->reset_delay_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363
364 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800365 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366
Matt Wagantall8bec3662012-01-25 11:06:13 -0800367 /* Prevent core memory from collapsing when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700368 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800369
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 /* Return clocks to their state before this function. */
371 restore_clocks(fs);
372
373 fs->is_enabled = true;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800374 return 0;
375
376err:
377 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 return rc;
379}
380
381static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
382{
383 struct footswitch *fs = rdev_get_drvdata(rdev);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700384 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 uint32_t regval, rc = 0;
386
Matt Wagantall88edea92011-07-21 10:29:56 -0700387 /* Return early if already disabled. */
388 regval = readl_relaxed(fs->gfs_ctl_reg);
389 if ((regval & ENABLE_BIT) == 0)
390 return 0;
391
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 /* Make sure required clocks are on at the correct rates. */
393 rc = setup_clocks(fs);
394 if (rc)
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800395 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Matt Wagantall8bec3662012-01-25 11:06:13 -0800397 /* Allow core memory to collapse when its clock is gated. */
Matt Wagantall76377792013-04-10 19:37:58 -0700398 clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN_MEM);
Matt Wagantall8bec3662012-01-25 11:06:13 -0800399
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 /* Halt all bus ports in the power domain. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800401 if (fs->bus_port0) {
402 rc = msm_bus_axi_porthalt(fs->bus_port0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 if (rc) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800404 pr_err("%s port 0 halt failed.\n", fs->desc.name);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800405 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 }
407 }
408
409 /* Disable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800410 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
412 /*
413 * Assert resets for all clocks in the clock domain so that
414 * outputs settle prior to clamping.
415 */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800416 for (clock = fs->clk_data; clock->clk; clock++)
Stephen Boyd0c629382011-12-28 19:15:57 -0800417 ; /* Do nothing */
418 for (clock--; clock >= fs->clk_data; clock--)
Matt Wagantallb82a5132011-12-12 22:26:41 -0800419 clk_reset(clock->clk, CLK_RESET_ASSERT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420 /* Wait for synchronous resets to propagate. */
Matt Wagantallb82a5132011-12-12 22:26:41 -0800421 udelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422
423 /*
424 * Clamp the I/O ports of the core to ensure the values
425 * remain fixed while the core is collapsed.
426 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427 regval |= CLAMP_BIT;
428 writel_relaxed(regval, fs->gfs_ctl_reg);
429
430 /* Collapse the power rail at the footswitch. */
431 regval &= ~ENABLE_BIT;
432 writel_relaxed(regval, fs->gfs_ctl_reg);
433
434 /* Re-enable core clock. */
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800435 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436
437 /* Return clocks to their state before this function. */
438 restore_clocks(fs);
439
440 fs->is_enabled = false;
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800441 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800443err:
Matt Wagantall76377792013-04-10 19:37:58 -0700444 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
Stephen Boyd2fc19e82011-12-07 17:38:38 -0800445 restore_clocks(fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446 return rc;
447}
448
Matt Wagantall6a118582013-04-01 16:18:37 -0700449static void force_bus_clocks(bool enforce)
450{
451 static struct msm_rpm_iv_pair iv;
452 int ret;
453
454 if (enforce) {
455 iv.id = MSM_RPM_STATUS_ID_RPM_CTL;
456 ret = msm_rpm_get_status(&iv, 1);
457 if (ret)
458 pr_err("Failed to read RPM_CTL resource status\n");
459
460 iv.id = MSM_RPM_ID_RPM_CTL;
461 iv.value |= BIT(6);
462 } else {
463 iv.id = MSM_RPM_ID_RPM_CTL;
464 iv.value &= ~BIT(6);
465 }
466
467 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
468 if (ret)
469 pr_err("Force bus clocks request=%d failed\n", enforce);
470}
471
472static int gfx3d_8064_footswitch_enable(struct regulator_dev *rdev)
473{
474 struct footswitch *fs = rdev_get_drvdata(rdev);
475 struct fs_clk_data *clock;
476 uint32_t regval, rc = 0;
477
478 mutex_lock(&claim_lock);
479 fs->is_claimed = true;
480 mutex_unlock(&claim_lock);
481
482 /* Return early if already enabled. */
483 regval = readl_relaxed(fs->gfs_ctl_reg);
484 if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT)
485 return 0;
486
487 /* Un-halt all bus ports in the power domain. */
488 if (fs->bus_port0) {
489 rc = msm_bus_axi_portunhalt(fs->bus_port0);
490 if (rc) {
491 pr_err("%s port 0 unhalt failed.\n", fs->desc.name);
492 goto err;
493 }
494 }
495 if (fs->bus_port1) {
496 rc = msm_bus_axi_portunhalt(fs->bus_port1);
497 if (rc) {
498 pr_err("%s port 1 unhalt failed.\n", fs->desc.name);
499 goto err_port2_halt;
500 }
501 }
502
503 /* Apply AFAB/EBI clock limits. */
504 force_bus_clocks(true);
505
506 /* Enable the power rail at the footswitch. */
507 regval |= ENABLE_BIT;
508 writel_relaxed(regval, fs->gfs_ctl_reg);
509 /* Wait for the rail to fully charge. */
510 mb();
511 udelay(1);
512
513 /* Make sure required clocks are on at the correct rates. */
514 rc = setup_clocks(fs);
515 if (rc)
516 goto err_setup_clocks;
517
518 /*
519 * (Re-)Assert resets for all clocks in the clock domain, since
520 * footswitch_enable() is first called before footswitch_disable()
521 * and resets should be asserted before power is restored.
522 */
523 for (clock = fs->clk_data; clock->clk; clock++)
524 ; /* Do nothing */
525 for (clock--; clock >= fs->clk_data; clock--)
526 clk_reset(clock->clk, CLK_RESET_ASSERT);
527 /* Wait for synchronous resets to propagate. */
528 udelay(fs->reset_delay_us);
529
530 /* Un-clamp the I/O ports. */
531 regval &= ~CLAMP_BIT;
532 writel_relaxed(regval, fs->gfs_ctl_reg);
533
534 /* Deassert resets for all clocks in the power domain. */
535 for (clock = fs->clk_data; clock->clk; clock++)
536 clk_reset(clock->clk, CLK_RESET_DEASSERT);
537
538 /* Prevent core memory from collapsing when its clock is gated. */
539 clk_set_flags(fs->core_clk, CLKFLAG_RETAIN_MEM);
540
541 /* Return clocks to their state before this function. */
542 restore_clocks(fs);
543
544 /* Remove AFAB/EBI clock limits after any transients have settled. */
545 udelay(30);
546 force_bus_clocks(false);
547
548 fs->is_enabled = true;
549 return 0;
550
551err_setup_clocks:
552 regval &= ~ENABLE_BIT;
553 writel_relaxed(regval, fs->gfs_ctl_reg);
554 force_bus_clocks(false);
555 msm_bus_axi_porthalt(fs->bus_port1);
556err_port2_halt:
557 msm_bus_axi_porthalt(fs->bus_port0);
558err:
559 return rc;
560}
561
562
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563static struct regulator_ops standard_fs_ops = {
564 .is_enabled = footswitch_is_enabled,
565 .enable = footswitch_enable,
566 .disable = footswitch_disable,
567};
568
569static struct regulator_ops gfx2d_fs_ops = {
570 .is_enabled = footswitch_is_enabled,
571 .enable = gfx2d_footswitch_enable,
572 .disable = gfx2d_footswitch_disable,
573};
574
Matt Wagantall6a118582013-04-01 16:18:37 -0700575static struct regulator_ops gfx3d_8064_fs_ops = {
576 .is_enabled = footswitch_is_enabled,
577 .enable = gfx3d_8064_footswitch_enable,
578 .disable = footswitch_disable,
579};
580
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700581#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582 [(_id)] = { \
583 .desc = { \
584 .id = (_id), \
585 .name = (_name), \
586 .ops = (_ops), \
587 .type = REGULATOR_VOLTAGE, \
588 .owner = THIS_MODULE, \
589 }, \
590 .gfs_ctl_reg = (_gfs_ctl_reg), \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591 }
592static struct footswitch footswitches[] = {
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700593 FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG),
594 FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG),
Matt Wagantall6a118582013-04-01 16:18:37 -0700595 FOOTSWITCH(FS_GFX3D_8064, "fs_gfx3d", &gfx3d_8064_fs_ops,
596 GFX3D_GFS_CTL_REG),
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700597 FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG),
598 FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG),
599 FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops, MDP_GFS_CTL_REG),
600 FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops, ROT_GFS_CTL_REG),
601 FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops, VED_GFS_CTL_REG),
602 FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops, VFE_GFS_CTL_REG),
603 FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops, VPE_GFS_CTL_REG),
604 FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops, VCAP_GFS_CTL_REG),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605};
606
607static int footswitch_probe(struct platform_device *pdev)
608{
609 struct footswitch *fs;
610 struct regulator_init_data *init_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700611 struct fs_driver_data *driver_data;
612 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700613 uint32_t regval, rc = 0;
614
615 if (pdev == NULL)
616 return -EINVAL;
617
618 if (pdev->id >= MAX_FS)
619 return -ENODEV;
620
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621 init_data = pdev->dev.platform_data;
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700622 driver_data = init_data->driver_data;
623 fs = &footswitches[pdev->id];
624 fs->clk_data = driver_data->clks;
625 fs->bus_port0 = driver_data->bus_port0;
626 fs->bus_port1 = driver_data->bus_port1;
Matt Wagantallcd6f4652013-04-01 15:22:47 -0700627 fs->reset_delay_us =
628 driver_data->reset_delay_us ? : DEFAULT_RESET_DELAY_US;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700629
Matt Wagantallb82a5132011-12-12 22:26:41 -0800630 for (clock = fs->clk_data; clock->name; clock++) {
631 clock->clk = clk_get(&pdev->dev, clock->name);
632 if (IS_ERR(clock->clk)) {
633 rc = PTR_ERR(clock->clk);
Matt Wagantall27fa2822012-02-22 18:43:27 -0800634 pr_err("%s clk_get(%s) failed\n", fs->desc.name,
635 clock->name);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800636 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637 }
Matt Wagantallb82a5132011-12-12 22:26:41 -0800638 if (!strncmp(clock->name, "core_clk", 8))
639 fs->core_clk = clock->clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640 }
641
642 /*
643 * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all
644 * after enabling the footswitch. Also ensure the retention bit is
645 * clear so disabling the footswitch will power-collapse the core.
646 */
647 regval = readl_relaxed(fs->gfs_ctl_reg);
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700648 regval |= GFS_DELAY_CNT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 regval &= ~RETENTION_BIT;
650 writel_relaxed(regval, fs->gfs_ctl_reg);
651
Rajendra Nayak11eafc62011-11-18 16:47:19 +0530652 fs->rdev = regulator_register(&fs->desc, &pdev->dev,
653 init_data, fs, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654 if (IS_ERR(footswitches[pdev->id].rdev)) {
Matt Wagantallb82a5132011-12-12 22:26:41 -0800655 pr_err("regulator_register(\"%s\") failed\n",
656 fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657 rc = PTR_ERR(footswitches[pdev->id].rdev);
Matt Wagantallb82a5132011-12-12 22:26:41 -0800658 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659 }
660
661 return 0;
662
Matt Wagantallb82a5132011-12-12 22:26:41 -0800663err:
664 for (clock = fs->clk_data; clock->clk; clock++)
665 clk_put(clock->clk);
666
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667 return rc;
668}
669
670static int __devexit footswitch_remove(struct platform_device *pdev)
671{
672 struct footswitch *fs = &footswitches[pdev->id];
Matt Wagantall1f65d9d2012-04-25 14:24:20 -0700673 struct fs_clk_data *clock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674
Matt Wagantallb82a5132011-12-12 22:26:41 -0800675 for (clock = fs->clk_data; clock->clk; clock++)
676 clk_put(clock->clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677 regulator_unregister(fs->rdev);
678
679 return 0;
680}
681
682static struct platform_driver footswitch_driver = {
683 .probe = footswitch_probe,
684 .remove = __devexit_p(footswitch_remove),
685 .driver = {
Matt Wagantall49722712011-08-17 18:50:53 -0700686 .name = "footswitch-8x60",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687 .owner = THIS_MODULE,
688 },
689};
690
691static int __init late_footswitch_init(void)
692{
693 int i;
694
695 mutex_lock(&claim_lock);
696 /* Turn off all registered but unused footswitches. */
697 for (i = 0; i < ARRAY_SIZE(footswitches); i++)
698 if (footswitches[i].rdev && !footswitches[i].is_claimed)
Matt Wagantall7a261362011-07-14 19:07:10 -0700699 footswitches[i].rdev->desc->ops->
700 disable(footswitches[i].rdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701 mutex_unlock(&claim_lock);
702
703 return 0;
704}
705late_initcall(late_footswitch_init);
706
707static int __init footswitch_init(void)
708{
709 return platform_driver_register(&footswitch_driver);
710}
711subsys_initcall(footswitch_init);
712
713static void __exit footswitch_exit(void)
714{
715 platform_driver_unregister(&footswitch_driver);
716}
717module_exit(footswitch_exit);
718
719MODULE_LICENSE("GPL v2");
720MODULE_DESCRIPTION("MSM8x60 rail footswitch");
721MODULE_ALIAS("platform:footswitch-msm8x60");