aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/powerdetect.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/powerdetect.c')
-rw-r--r--arch/arm/mach-tegra/powerdetect.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/powerdetect.c b/arch/arm/mach-tegra/powerdetect.c
new file mode 100644
index 00000000000..6a2a5779458
--- /dev/null
+++ b/arch/arm/mach-tegra/powerdetect.c
@@ -0,0 +1,348 @@
1/*
2 * arch/arm/mach-tegra/powerdetect.c
3 *
4 * Copyright (c) 2011, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include <linux/kernel.h>
21#include <linux/delay.h>
22#include <linux/spinlock.h>
23#include <linux/io.h>
24#include <linux/err.h>
25#include <linux/notifier.h>
26#include <linux/regulator/consumer.h>
27
28#include <mach/iomap.h>
29
30#include "board.h"
31#include "fuse.h"
32
33#define PMC_PWR_IO_DISABLE 0x44
34#define PMC_PWR_DET_ENABLE 0x48
35#define PMC_PWR_DET_LATCH 0x4C
36#define PMC_PWR_DET_VAL 0xE4
37
38struct pwr_detect_cell {
39 const char *reg_id;
40 u32 pwrdet_mask;
41 u32 pwrio_mask;
42 u32 package_mask;
43
44 struct notifier_block regulator_nb;
45};
46
47static bool pwrdet_rails_found;
48static bool pwrdet_always_on;
49static bool pwrio_always_on;
50static u32 pwrdet_val;
51static u32 pwrio_val;
52static u32 pwrio_disabled_mask;
53
54static DEFINE_SPINLOCK(pwr_lock);
55
56static void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
57
58static inline void pmc_writel(u32 val, unsigned long addr)
59{
60 writel(val, (u32)pmc_base + addr);
61}
62static inline u32 pmc_readl(unsigned long addr)
63{
64 return readl((u32)pmc_base + addr);
65}
66
67
68#define POWER_CELL(_reg_id, _pwrdet_mask, _pwrio_mask, _package_mask) \
69 { \
70 .reg_id = _reg_id, \
71 .pwrdet_mask = _pwrdet_mask, \
72 .pwrio_mask = _pwrio_mask, \
73 .package_mask = _package_mask, \
74 }
75
76/* Some IO pads does not have power detect cells, but still can/should be
77 * turned off when no power - set pwrdet_mask=0 for such pads */
78static struct pwr_detect_cell pwr_detect_cells[] = {
79 POWER_CELL("pwrdet_nand", (0x1 << 1), (0x1 << 1), 0xFFFFFFFF),
80 POWER_CELL("pwrdet_uart", (0x1 << 2), (0x1 << 2), 0xFFFFFFFF),
81 POWER_CELL("pwrdet_bb", (0x1 << 3), (0x1 << 3), 0xFFFFFFFF),
82#ifdef CONFIG_ARCH_TEGRA_2x_SOC
83 POWER_CELL("pwrdet_vi", 0, (0x1 << 4), 0xFFFFFFFF),
84#else
85 /* Tegra3 VI is connected on MID package only (id = 1, mask = 0x2) */
86 POWER_CELL("pwrdet_vi", 0, (0x1 << 4), 0x00000002),
87#endif
88 POWER_CELL("pwrdet_audio", (0x1 << 5), (0x1 << 5), 0xFFFFFFFF),
89 POWER_CELL("pwrdet_lcd", (0x1 << 6), (0x1 << 6), 0xFFFFFFFF),
90#ifdef CONFIG_ARCH_TEGRA_2x_SOC
91 POWER_CELL("pwrdet_sd", 0, (0x1 << 8), 0xFFFFFFFF),
92#endif
93 POWER_CELL("pwrdet_mipi", 0, (0x1 << 9), 0xFFFFFFFF),
94#ifndef CONFIG_ARCH_TEGRA_2x_SOC
95 POWER_CELL("pwrdet_cam", (0x1 << 10), (0x1 << 10), 0xFFFFFFFF),
96 POWER_CELL("pwrdet_pex_ctl", (0x1 << 11), (0x1 << 11), 0xFFFFFFFF),
97 POWER_CELL("pwrdet_sdmmc1", (0x1 << 12), (0x1 << 12), 0xFFFFFFFF),
98 POWER_CELL("pwrdet_sdmmc3", (0x1 << 13), (0x1 << 13), 0xFFFFFFFF),
99 POWER_CELL("pwrdet_sdmmc4", 0, (0x1 << 14), 0xFFFFFFFF),
100#endif
101};
102
103static void pwr_detect_reset(u32 pwrdet_mask)
104{
105 pmc_writel(pwrdet_mask, PMC_PWR_DET_ENABLE);
106 barrier();
107 pmc_writel(pwrdet_mask, PMC_PWR_DET_VAL);
108
109 pmc_readl(PMC_PWR_DET_VAL);
110 pmc_writel(0, PMC_PWR_DET_ENABLE);
111}
112
113static void pwr_detect_start(u32 pwrdet_mask)
114{
115 pmc_writel(pwrdet_mask, PMC_PWR_DET_ENABLE);
116 udelay(4);
117
118 pmc_writel(1, PMC_PWR_DET_LATCH);
119 pmc_readl(PMC_PWR_DET_LATCH);
120}
121
122static void pwr_detect_latch(void)
123{
124 pmc_writel(0, PMC_PWR_DET_LATCH);
125
126 pmc_readl(PMC_PWR_DET_VAL);
127 pmc_writel(0, PMC_PWR_DET_ENABLE);
128}
129
130static void pwr_io_enable(u32 pwrio_mask)
131{
132 u32 val = pmc_readl(PMC_PWR_IO_DISABLE);
133 val &= ~pwrio_mask;
134 pmc_writel(val, PMC_PWR_IO_DISABLE);
135}
136
137static void pwr_io_disable(u32 pwrio_mask)
138{
139 u32 val = pmc_readl(PMC_PWR_IO_DISABLE);
140 val |= pwrio_mask;
141 pmc_writel(val, PMC_PWR_IO_DISABLE);
142}
143
144static int pwrdet_always_on_set(const char *arg, const struct kernel_param *kp)
145{
146 int ret;
147 unsigned long flags;
148
149 spin_lock_irqsave(&pwr_lock, flags);
150
151 ret = param_set_bool(arg, kp);
152 if (ret) {
153 spin_unlock_irqrestore(&pwr_lock, flags);
154 return ret;
155 }
156
157 if (pwrdet_always_on)
158 pwr_detect_start(0xFFFFFFFF);
159 else
160 pwr_detect_latch();
161
162 spin_unlock_irqrestore(&pwr_lock, flags);
163 return 0;
164}
165
166static int pwrio_always_on_set(const char *arg, const struct kernel_param *kp)
167{
168 int ret;
169 unsigned long flags;
170
171 spin_lock_irqsave(&pwr_lock, flags);
172
173 ret = param_set_bool(arg, kp);
174 if (ret) {
175 spin_unlock_irqrestore(&pwr_lock, flags);
176 return ret;
177 }
178
179 if (pwrio_always_on)
180 pwr_io_enable(0xFFFFFFFF);
181 else
182 pwr_io_disable(pwrio_disabled_mask);
183
184 spin_unlock_irqrestore(&pwr_lock, flags);
185 return 0;
186}
187
188static int pwrdet_always_on_get(char *buffer, const struct kernel_param *kp)
189{
190 return param_get_bool(buffer, kp);
191}
192
193static struct kernel_param_ops pwrdet_always_on_ops = {
194 .set = pwrdet_always_on_set,
195 .get = pwrdet_always_on_get,
196};
197static struct kernel_param_ops pwrio_always_on_ops = {
198 .set = pwrio_always_on_set,
199 .get = pwrdet_always_on_get,
200};
201module_param_cb(pwrdet_always_on, &pwrdet_always_on_ops,
202 &pwrdet_always_on, 0644);
203module_param_cb(pwrio_always_on, &pwrio_always_on_ops,
204 &pwrio_always_on, 0644);
205
206static int pwrdet_val_get(char *buffer, const struct kernel_param *kp)
207{
208 pwrdet_val = pmc_readl(PMC_PWR_DET_VAL);
209 return param_get_ulong(buffer, kp);
210}
211static struct kernel_param_ops pwrdet_val_ops = {
212 .get = pwrdet_val_get,
213};
214module_param_cb(pwrdet_val, &pwrdet_val_ops, &pwrdet_val, 0444);
215
216static int pwrio_val_get(char *buffer, const struct kernel_param *kp)
217{
218 pwrio_val = pmc_readl(PMC_PWR_IO_DISABLE);
219 return param_get_ulong(buffer, kp);
220}
221static struct kernel_param_ops pwrio_val_ops = {
222 .get = pwrio_val_get,
223};
224module_param_cb(pwrio_val, &pwrio_val_ops, &pwrio_val, 0444);
225
226
227static int pwrdet_notify_cb(
228 struct notifier_block *nb, unsigned long event, void *v)
229{
230 unsigned long flags;
231 struct pwr_detect_cell *cell;
232
233 if (!pwrdet_rails_found)
234 return NOTIFY_OK;
235
236 cell = container_of(nb, struct pwr_detect_cell, regulator_nb);
237
238 spin_lock_irqsave(&pwr_lock, flags);
239
240 if (event & REGULATOR_EVENT_PRE_ENABLE) {
241 pwrio_disabled_mask &= ~cell->pwrio_mask;
242 if (!pwrio_always_on)
243 pwr_io_enable(cell->pwrio_mask);
244 }
245 if (event & (REGULATOR_EVENT_PRE_ENABLE |
246 REGULATOR_EVENT_OUT_PRECHANGE)) {
247 if (!pwrdet_always_on && cell->pwrdet_mask)
248 pwr_detect_reset(cell->pwrdet_mask);
249 }
250 if (event & (REGULATOR_EVENT_POST_ENABLE |
251 REGULATOR_EVENT_OUT_POSTCHANGE)) {
252 if (!pwrdet_always_on && cell->pwrdet_mask) {
253 pwr_detect_start(cell->pwrdet_mask);
254 pwr_detect_latch();
255 }
256 }
257 if (event & (REGULATOR_EVENT_DISABLE |
258 REGULATOR_EVENT_FORCE_DISABLE)) {
259 pwrio_disabled_mask |= cell->pwrio_mask;
260 if (!pwrio_always_on)
261 pwr_io_disable(cell->pwrio_mask);
262 }
263
264 pr_debug("tegra: %s: event %lu, pwrdet 0x%x, pwrio 0x%x\n",
265 cell->reg_id, event,
266 pmc_readl(PMC_PWR_DET_VAL), pmc_readl(PMC_PWR_IO_DISABLE));
267 spin_unlock_irqrestore(&pwr_lock, flags);
268
269 return NOTIFY_OK;
270}
271
272static int __init pwr_detect_cell_init_one(
273 struct pwr_detect_cell *cell, u32 *disabled_mask)
274{
275 int ret;
276 struct regulator *regulator = regulator_get(NULL, cell->reg_id);
277
278 if (IS_ERR(regulator))
279 return PTR_ERR(regulator);
280
281 cell->regulator_nb.notifier_call = pwrdet_notify_cb;
282 ret = regulator_register_notifier(regulator, &cell->regulator_nb);
283 if (ret) {
284 regulator_put(regulator);
285 return ret;
286 }
287
288 if (!regulator_is_enabled(regulator))
289 *disabled_mask |= cell->pwrio_mask;
290
291 regulator_put(regulator);
292 return 0;
293}
294
295int __init tegra_pwr_detect_cell_init(void)
296{
297 int i, ret;
298 u32 package_mask;
299 unsigned long flags;
300 bool rails_found = true;
301
302 i = tegra_package_id();
303 if ((i != -1) && (i & (~0x1F))) {
304 pr_err("tegra: not supported package id %d - io power detection"
305 " is left always on\n", i);
306 return 0;
307 }
308 package_mask = (i == -1) ? i : (0x1 << i);
309
310 for (i = 0; i < ARRAY_SIZE(pwr_detect_cells); i++) {
311 struct pwr_detect_cell *cell = &pwr_detect_cells[i];
312
313 if (!(cell->package_mask & package_mask)) {
314 pwrio_disabled_mask |= cell->pwrio_mask;
315 continue;
316 }
317
318 ret = pwr_detect_cell_init_one(cell, &pwrio_disabled_mask);
319 if (ret) {
320 pr_err("tegra: failed to map regulator to power detect"
321 " cell %s(%d)\n", cell->reg_id, ret);
322 rails_found = false;
323 }
324 }
325
326 if (!rails_found) {
327 pr_err("tegra: failed regulators mapping - io power detection"
328 " is left always on\n");
329 return 0;
330 }
331 pwrdet_rails_found = true;
332
333 /* Latch initial i/o power levels, disable all detection cells
334 and not powered interfaces */
335 spin_lock_irqsave(&pwr_lock, flags);
336 if (!pwrdet_always_on)
337 pwr_detect_latch();
338 if (!pwrio_always_on)
339 pwr_io_disable(pwrio_disabled_mask);
340 spin_unlock_irqrestore(&pwr_lock, flags);
341
342 pr_info("tegra: started io power detection dynamic control\n");
343 pr_info("tegra: NO_IO_POWER setting 0x%x\n", pwrio_disabled_mask);
344
345 return 0;
346}
347
348fs_initcall(tegra_pwr_detect_cell_init);