diff options
Diffstat (limited to 'arch/arm/mach-tegra/powergate.c')
-rw-r--r-- | arch/arm/mach-tegra/powergate.c | 723 |
1 files changed, 688 insertions, 35 deletions
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 3cee9aa1f2c..76d264fcad0 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * drivers/powergate/tegra-powergate.c | 2 | * drivers/powergate/tegra-powergate.c |
3 | * | 3 | * |
4 | * Copyright (c) 2010 Google, Inc | 4 | * Copyright (c) 2010 Google, Inc |
5 | * Copyright (C) 2011 NVIDIA Corporation. | ||
5 | * | 6 | * |
6 | * Author: | 7 | * Author: |
7 | * Colin Cross <ccross@google.com> | 8 | * Colin Cross <ccross@google.com> |
@@ -31,15 +32,138 @@ | |||
31 | #include <mach/iomap.h> | 32 | #include <mach/iomap.h> |
32 | #include <mach/powergate.h> | 33 | #include <mach/powergate.h> |
33 | 34 | ||
35 | #include "clock.h" | ||
36 | |||
34 | #define PWRGATE_TOGGLE 0x30 | 37 | #define PWRGATE_TOGGLE 0x30 |
35 | #define PWRGATE_TOGGLE_START (1 << 8) | 38 | #define PWRGATE_TOGGLE_START (1 << 8) |
36 | 39 | ||
37 | #define REMOVE_CLAMPING 0x34 | 40 | #define REMOVE_CLAMPING 0x34 |
38 | 41 | ||
39 | #define PWRGATE_STATUS 0x38 | 42 | #define PWRGATE_STATUS 0x38 |
40 | 43 | ||
44 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
45 | enum mc_client { | ||
46 | MC_CLIENT_AFI = 0, | ||
47 | MC_CLIENT_AVPC = 1, | ||
48 | MC_CLIENT_DC = 2, | ||
49 | MC_CLIENT_DCB = 3, | ||
50 | MC_CLIENT_EPP = 4, | ||
51 | MC_CLIENT_G2 = 5, | ||
52 | MC_CLIENT_HC = 6, | ||
53 | MC_CLIENT_HDA = 7, | ||
54 | MC_CLIENT_ISP = 8, | ||
55 | MC_CLIENT_MPCORE = 9, | ||
56 | MC_CLIENT_MPCORELP = 10, | ||
57 | MC_CLIENT_MPE = 11, | ||
58 | MC_CLIENT_NV = 12, | ||
59 | MC_CLIENT_NV2 = 13, | ||
60 | MC_CLIENT_PPCS = 14, | ||
61 | MC_CLIENT_SATA = 15, | ||
62 | MC_CLIENT_VDE = 16, | ||
63 | MC_CLIENT_VI = 17, | ||
64 | MC_CLIENT_LAST = -1, | ||
65 | }; | ||
66 | #else | ||
67 | enum mc_client { | ||
68 | MC_CLIENT_AVPC = 0, | ||
69 | MC_CLIENT_DC = 1, | ||
70 | MC_CLIENT_DCB = 2, | ||
71 | MC_CLIENT_EPP = 3, | ||
72 | MC_CLIENT_G2 = 4, | ||
73 | MC_CLIENT_HC = 5, | ||
74 | MC_CLIENT_ISP = 6, | ||
75 | MC_CLIENT_MPCORE = 7, | ||
76 | MC_CLIENT_MPEA = 8, | ||
77 | MC_CLIENT_MPEB = 9, | ||
78 | MC_CLIENT_MPEC = 10, | ||
79 | MC_CLIENT_NV = 11, | ||
80 | MC_CLIENT_PPCS = 12, | ||
81 | MC_CLIENT_VDE = 13, | ||
82 | MC_CLIENT_VI = 14, | ||
83 | MC_CLIENT_LAST = -1, | ||
84 | MC_CLIENT_AFI = MC_CLIENT_LAST, | ||
85 | }; | ||
86 | #endif | ||
87 | |||
88 | #define MAX_CLK_EN_NUM 4 | ||
89 | |||
41 | static DEFINE_SPINLOCK(tegra_powergate_lock); | 90 | static DEFINE_SPINLOCK(tegra_powergate_lock); |
42 | 91 | ||
92 | #define MAX_HOTRESET_CLIENT_NUM 4 | ||
93 | |||
94 | enum clk_type { | ||
95 | CLK_AND_RST, | ||
96 | RST_ONLY, | ||
97 | CLK_ONLY, | ||
98 | }; | ||
99 | |||
100 | struct partition_clk_info { | ||
101 | const char *clk_name; | ||
102 | enum clk_type clk_type; | ||
103 | /* true if clk is only used in assert/deassert reset and not while enable-den*/ | ||
104 | struct clk *clk_ptr; | ||
105 | }; | ||
106 | |||
107 | struct powergate_partition { | ||
108 | const char *name; | ||
109 | enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM]; | ||
110 | struct partition_clk_info clk_info[MAX_CLK_EN_NUM]; | ||
111 | }; | ||
112 | |||
113 | static struct powergate_partition powergate_partition_info[TEGRA_NUM_POWERGATE] = { | ||
114 | [TEGRA_POWERGATE_CPU] = { "cpu0", {MC_CLIENT_LAST}, }, | ||
115 | [TEGRA_POWERGATE_L2] = { "l2", {MC_CLIENT_LAST}, }, | ||
116 | [TEGRA_POWERGATE_3D] = { "3d0", | ||
117 | {MC_CLIENT_NV, MC_CLIENT_LAST}, | ||
118 | {{"3d", CLK_AND_RST} }, }, | ||
119 | [TEGRA_POWERGATE_PCIE] = { "pcie", | ||
120 | {MC_CLIENT_AFI, MC_CLIENT_LAST}, | ||
121 | {{"afi", CLK_AND_RST}, | ||
122 | {"pcie", CLK_AND_RST}, | ||
123 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
124 | {"cml0", CLK_ONLY}, | ||
125 | #endif | ||
126 | {"pciex", RST_ONLY} }, }, | ||
127 | [TEGRA_POWERGATE_VDEC] = { "vde", | ||
128 | {MC_CLIENT_VDE, MC_CLIENT_LAST}, | ||
129 | {{"vde", CLK_AND_RST} }, }, | ||
130 | [TEGRA_POWERGATE_MPE] = { "mpe", | ||
131 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
132 | {MC_CLIENT_MPE, MC_CLIENT_LAST}, | ||
133 | #else | ||
134 | {MC_CLIENT_MPEA, MC_CLIENT_MPEB, | ||
135 | MC_CLIENT_MPEC, MC_CLIENT_LAST}, | ||
136 | #endif | ||
137 | {{"mpe", CLK_AND_RST} }, }, | ||
138 | [TEGRA_POWERGATE_VENC] = { "ve", | ||
139 | {MC_CLIENT_ISP, MC_CLIENT_VI, MC_CLIENT_LAST}, | ||
140 | {{"isp", CLK_AND_RST}, | ||
141 | {"vi", CLK_AND_RST}, | ||
142 | {"csi", CLK_AND_RST} }, }, | ||
143 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
144 | [TEGRA_POWERGATE_CPU1] = { "cpu1", {MC_CLIENT_LAST}, }, | ||
145 | [TEGRA_POWERGATE_CPU2] = { "cpu2", {MC_CLIENT_LAST}, }, | ||
146 | [TEGRA_POWERGATE_CPU3] = { "cpu3", {MC_CLIENT_LAST}, }, | ||
147 | [TEGRA_POWERGATE_CELP] = { "celp", {MC_CLIENT_LAST}, }, | ||
148 | [TEGRA_POWERGATE_SATA] = { "sata", {MC_CLIENT_SATA, MC_CLIENT_LAST}, | ||
149 | {{"sata", CLK_AND_RST}, | ||
150 | {"sata_oob", CLK_AND_RST}, | ||
151 | {"cml1", CLK_ONLY}, | ||
152 | {"sata_cold", RST_ONLY} }, }, | ||
153 | [TEGRA_POWERGATE_3D1] = { "3d1", | ||
154 | {MC_CLIENT_NV2, MC_CLIENT_LAST}, | ||
155 | {{"3d2", CLK_AND_RST} }, }, | ||
156 | [TEGRA_POWERGATE_HEG] = { "heg", | ||
157 | {MC_CLIENT_G2, MC_CLIENT_EPP, | ||
158 | MC_CLIENT_HC, | ||
159 | MC_CLIENT_LAST}, | ||
160 | {{"2d", CLK_AND_RST}, | ||
161 | {"epp", CLK_AND_RST}, | ||
162 | {"host1x", CLK_AND_RST}, | ||
163 | {"3d", RST_ONLY} }, }, | ||
164 | #endif | ||
165 | }; | ||
166 | |||
43 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | 167 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); |
44 | 168 | ||
45 | static u32 pmc_read(unsigned long reg) | 169 | static u32 pmc_read(unsigned long reg) |
@@ -52,40 +176,306 @@ static void pmc_write(u32 val, unsigned long reg) | |||
52 | writel(val, pmc + reg); | 176 | writel(val, pmc + reg); |
53 | } | 177 | } |
54 | 178 | ||
179 | static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE); | ||
180 | |||
181 | static u32 mc_read(unsigned long reg) | ||
182 | { | ||
183 | return readl(mc + reg); | ||
184 | } | ||
185 | |||
186 | static void mc_write(u32 val, unsigned long reg) | ||
187 | { | ||
188 | writel(val, mc + reg); | ||
189 | } | ||
190 | |||
191 | #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) | ||
192 | |||
193 | #define MC_CLIENT_HOTRESET_CTRL 0x200 | ||
194 | #define MC_CLIENT_HOTRESET_STAT 0x204 | ||
195 | |||
196 | static void mc_flush(int id) | ||
197 | { | ||
198 | u32 idx, rst_ctrl, rst_stat; | ||
199 | enum mc_client mcClientBit; | ||
200 | unsigned long flags; | ||
201 | |||
202 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
203 | |||
204 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
205 | mcClientBit = powergate_partition_info[id].hot_reset_clients[idx]; | ||
206 | if (mcClientBit == MC_CLIENT_LAST) | ||
207 | break; | ||
208 | |||
209 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
210 | rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL); | ||
211 | rst_ctrl |= (1 << mcClientBit); | ||
212 | mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL); | ||
213 | |||
214 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
215 | |||
216 | do { | ||
217 | udelay(10); | ||
218 | rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT); | ||
219 | } while (!(rst_stat & (1 << mcClientBit))); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void mc_flush_done(int id) | ||
224 | { | ||
225 | u32 idx, rst_ctrl; | ||
226 | enum mc_client mcClientBit; | ||
227 | unsigned long flags; | ||
228 | |||
229 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
230 | |||
231 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
232 | mcClientBit = powergate_partition_info[id].hot_reset_clients[idx]; | ||
233 | if (mcClientBit == MC_CLIENT_LAST) | ||
234 | break; | ||
235 | |||
236 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
237 | |||
238 | rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL); | ||
239 | rst_ctrl &= ~(1 << mcClientBit); | ||
240 | mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL); | ||
241 | |||
242 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
243 | } | ||
244 | |||
245 | wmb(); | ||
246 | } | ||
247 | |||
248 | int tegra_powergate_mc_flush(int id) | ||
249 | { | ||
250 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | ||
251 | return -EINVAL; | ||
252 | mc_flush(id); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | int tegra_powergate_mc_flush_done(int id) | ||
257 | { | ||
258 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | ||
259 | return -EINVAL; | ||
260 | mc_flush_done(id); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | int tegra_powergate_mc_disable(int id) | ||
265 | { | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | int tegra_powergate_mc_enable(int id) | ||
270 | { | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | #else | ||
275 | |||
276 | #define MC_CLIENT_CTRL 0x100 | ||
277 | #define MC_CLIENT_HOTRESETN 0x104 | ||
278 | #define MC_CLIENT_ORRC_BASE 0x140 | ||
279 | |||
280 | int tegra_powergate_mc_disable(int id) | ||
281 | { | ||
282 | u32 idx, clt_ctrl, orrc_reg; | ||
283 | enum mc_client mcClientBit; | ||
284 | unsigned long flags; | ||
285 | |||
286 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) { | ||
287 | WARN_ON(1); | ||
288 | return -EINVAL; | ||
289 | } | ||
290 | |||
291 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
292 | mcClientBit = | ||
293 | powergate_partition_info[id].hot_reset_clients[idx]; | ||
294 | if (mcClientBit == MC_CLIENT_LAST) | ||
295 | break; | ||
296 | |||
297 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
298 | |||
299 | /* clear client enable bit */ | ||
300 | clt_ctrl = mc_read(MC_CLIENT_CTRL); | ||
301 | clt_ctrl &= ~(1 << mcClientBit); | ||
302 | mc_write(clt_ctrl, MC_CLIENT_CTRL); | ||
303 | |||
304 | /* read back to flush write */ | ||
305 | clt_ctrl = mc_read(MC_CLIENT_CTRL); | ||
306 | |||
307 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
308 | |||
309 | /* wait for outstanding requests to reach 0 */ | ||
310 | orrc_reg = MC_CLIENT_ORRC_BASE + (mcClientBit * 4); | ||
311 | while (mc_read(orrc_reg) != 0) | ||
312 | udelay(10); | ||
313 | } | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | int tegra_powergate_mc_flush(int id) | ||
318 | { | ||
319 | u32 idx, hot_rstn; | ||
320 | enum mc_client mcClientBit; | ||
321 | unsigned long flags; | ||
322 | |||
323 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) { | ||
324 | WARN_ON(1); | ||
325 | return -EINVAL; | ||
326 | } | ||
327 | |||
328 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
329 | mcClientBit = | ||
330 | powergate_partition_info[id].hot_reset_clients[idx]; | ||
331 | if (mcClientBit == MC_CLIENT_LAST) | ||
332 | break; | ||
333 | |||
334 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
335 | |||
336 | /* assert hotreset (client module is currently in reset) */ | ||
337 | hot_rstn = mc_read(MC_CLIENT_HOTRESETN); | ||
338 | hot_rstn &= ~(1 << mcClientBit); | ||
339 | mc_write(hot_rstn, MC_CLIENT_HOTRESETN); | ||
340 | |||
341 | /* read back to flush write */ | ||
342 | hot_rstn = mc_read(MC_CLIENT_HOTRESETN); | ||
343 | |||
344 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
345 | } | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | int tegra_powergate_mc_flush_done(int id) | ||
350 | { | ||
351 | u32 idx, hot_rstn; | ||
352 | enum mc_client mcClientBit; | ||
353 | unsigned long flags; | ||
354 | |||
355 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) { | ||
356 | WARN_ON(1); | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
361 | mcClientBit = | ||
362 | powergate_partition_info[id].hot_reset_clients[idx]; | ||
363 | if (mcClientBit == MC_CLIENT_LAST) | ||
364 | break; | ||
365 | |||
366 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
367 | |||
368 | /* deassert hotreset */ | ||
369 | hot_rstn = mc_read(MC_CLIENT_HOTRESETN); | ||
370 | hot_rstn |= (1 << mcClientBit); | ||
371 | mc_write(hot_rstn, MC_CLIENT_HOTRESETN); | ||
372 | |||
373 | /* read back to flush write */ | ||
374 | hot_rstn = mc_read(MC_CLIENT_HOTRESETN); | ||
375 | |||
376 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
377 | } | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | int tegra_powergate_mc_enable(int id) | ||
382 | { | ||
383 | u32 idx, clt_ctrl; | ||
384 | enum mc_client mcClientBit; | ||
385 | unsigned long flags; | ||
386 | |||
387 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) { | ||
388 | WARN_ON(1); | ||
389 | return -EINVAL; | ||
390 | } | ||
391 | |||
392 | for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) { | ||
393 | mcClientBit = | ||
394 | powergate_partition_info[id].hot_reset_clients[idx]; | ||
395 | if (mcClientBit == MC_CLIENT_LAST) | ||
396 | break; | ||
397 | |||
398 | spin_lock_irqsave(&tegra_powergate_lock, flags); | ||
399 | |||
400 | /* enable client */ | ||
401 | clt_ctrl = mc_read(MC_CLIENT_CTRL); | ||
402 | clt_ctrl |= (1 << mcClientBit); | ||
403 | mc_write(clt_ctrl, MC_CLIENT_CTRL); | ||
404 | |||
405 | /* read back to flush write */ | ||
406 | clt_ctrl = mc_read(MC_CLIENT_CTRL); | ||
407 | |||
408 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
409 | } | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static void mc_flush(int id) {} | ||
414 | static void mc_flush_done(int id) {} | ||
415 | #endif | ||
416 | |||
55 | static int tegra_powergate_set(int id, bool new_state) | 417 | static int tegra_powergate_set(int id, bool new_state) |
56 | { | 418 | { |
57 | bool status; | 419 | bool status; |
58 | unsigned long flags; | 420 | unsigned long flags; |
421 | /* 10us timeout for toggle operation if it takes affect*/ | ||
422 | int toggle_timeout = 10; | ||
423 | /* 100 * 10 = 1000us timeout for toggle command to take affect in case | ||
424 | of contention with h/w initiated CPU power gating */ | ||
425 | int contention_timeout = 100; | ||
59 | 426 | ||
60 | spin_lock_irqsave(&tegra_powergate_lock, flags); | 427 | spin_lock_irqsave(&tegra_powergate_lock, flags); |
61 | 428 | ||
62 | status = pmc_read(PWRGATE_STATUS) & (1 << id); | 429 | status = !!(pmc_read(PWRGATE_STATUS) & (1 << id)); |
63 | 430 | ||
64 | if (status == new_state) { | 431 | if (status == new_state) { |
65 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | 432 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); |
66 | return -EINVAL; | 433 | return 0; |
434 | } | ||
435 | |||
436 | if (TEGRA_IS_CPU_POWERGATE_ID(id)) { | ||
437 | /* CPU ungated in s/w only during boot/resume with outer | ||
438 | waiting loop and no contention from other CPUs */ | ||
439 | pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); | ||
440 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | ||
441 | return 0; | ||
67 | } | 442 | } |
68 | 443 | ||
69 | pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); | 444 | do { |
445 | pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); | ||
446 | do { | ||
447 | udelay(1); | ||
448 | status = !!(pmc_read(PWRGATE_STATUS) & (1 << id)); | ||
449 | |||
450 | toggle_timeout--; | ||
451 | } while ((status != new_state) && (toggle_timeout > 0)); | ||
452 | |||
453 | contention_timeout--; | ||
454 | } while ((status != new_state) && (contention_timeout > 0)); | ||
70 | 455 | ||
71 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); | 456 | spin_unlock_irqrestore(&tegra_powergate_lock, flags); |
72 | 457 | ||
458 | if (status != new_state) { | ||
459 | WARN(1, "Could not set powergate %d to %d", id, new_state); | ||
460 | return -EBUSY; | ||
461 | } | ||
462 | |||
73 | return 0; | 463 | return 0; |
74 | } | 464 | } |
75 | 465 | ||
76 | int tegra_powergate_power_on(int id) | 466 | static int unpowergate_module(int id) |
77 | { | 467 | { |
78 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | 468 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) |
79 | return -EINVAL; | 469 | return -EINVAL; |
80 | |||
81 | return tegra_powergate_set(id, true); | 470 | return tegra_powergate_set(id, true); |
82 | } | 471 | } |
83 | 472 | ||
84 | int tegra_powergate_power_off(int id) | 473 | static int powergate_module(int id) |
85 | { | 474 | { |
86 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | 475 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) |
87 | return -EINVAL; | 476 | return -EINVAL; |
88 | 477 | ||
478 | mc_flush(id); | ||
89 | return tegra_powergate_set(id, false); | 479 | return tegra_powergate_set(id, false); |
90 | } | 480 | } |
91 | 481 | ||
@@ -94,7 +484,7 @@ bool tegra_powergate_is_powered(int id) | |||
94 | u32 status; | 484 | u32 status; |
95 | 485 | ||
96 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | 486 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) |
97 | return -EINVAL; | 487 | return false; |
98 | 488 | ||
99 | status = pmc_read(PWRGATE_STATUS) & (1 << id); | 489 | status = pmc_read(PWRGATE_STATUS) & (1 << id); |
100 | return !!status; | 490 | return !!status; |
@@ -103,17 +493,16 @@ bool tegra_powergate_is_powered(int id) | |||
103 | int tegra_powergate_remove_clamping(int id) | 493 | int tegra_powergate_remove_clamping(int id) |
104 | { | 494 | { |
105 | u32 mask; | 495 | u32 mask; |
106 | |||
107 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | 496 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) |
108 | return -EINVAL; | 497 | return -EINVAL; |
109 | 498 | ||
110 | /* | 499 | /* |
111 | * Tegra 2 has a bug where PCIE and VDE clamping masks are | 500 | * PCIE and VDE clamping masks are swapped with respect to their |
112 | * swapped relatively to the partition ids | 501 | * partition ids |
113 | */ | 502 | */ |
114 | if (id == TEGRA_POWERGATE_VDEC) | 503 | if (id == TEGRA_POWERGATE_VDEC) |
115 | mask = (1 << TEGRA_POWERGATE_PCIE); | 504 | mask = (1 << TEGRA_POWERGATE_PCIE); |
116 | else if (id == TEGRA_POWERGATE_PCIE) | 505 | else if (id == TEGRA_POWERGATE_PCIE) |
117 | mask = (1 << TEGRA_POWERGATE_VDEC); | 506 | mask = (1 << TEGRA_POWERGATE_VDEC); |
118 | else | 507 | else |
119 | mask = (1 << id); | 508 | mask = (1 << id); |
@@ -123,20 +512,184 @@ int tegra_powergate_remove_clamping(int id) | |||
123 | return 0; | 512 | return 0; |
124 | } | 513 | } |
125 | 514 | ||
126 | /* Must be called with clk disabled, and returns with clk enabled */ | 515 | static void get_clk_info(int id) |
127 | int tegra_powergate_sequence_power_up(int id, struct clk *clk) | 516 | { |
517 | int idx; | ||
518 | |||
519 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
520 | if (!powergate_partition_info[id].clk_info[idx].clk_name) | ||
521 | break; | ||
522 | powergate_partition_info[id]. | ||
523 | clk_info[idx].clk_ptr = | ||
524 | tegra_get_clock_by_name( | ||
525 | powergate_partition_info[id].clk_info[idx].clk_name); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | static int partition_clk_enable(int id) | ||
530 | { | ||
531 | int ret; | ||
532 | u32 idx; | ||
533 | struct clk *clk; | ||
534 | struct partition_clk_info *clk_info; | ||
535 | |||
536 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
537 | |||
538 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
539 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
540 | clk = clk_info->clk_ptr; | ||
541 | if (!clk) | ||
542 | break; | ||
543 | |||
544 | if (clk_info->clk_type != RST_ONLY) { | ||
545 | ret = clk_enable(clk); | ||
546 | if (ret) | ||
547 | goto err_clk_en; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | return 0; | ||
552 | |||
553 | err_clk_en: | ||
554 | WARN(1, "Could not enable clk %s", clk->name); | ||
555 | while (idx--) { | ||
556 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
557 | if (clk_info->clk_type != RST_ONLY) | ||
558 | clk_disable(clk_info->clk_ptr); | ||
559 | } | ||
560 | |||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | static int is_partition_clk_disabled(int id) | ||
565 | { | ||
566 | u32 idx; | ||
567 | struct clk *clk; | ||
568 | struct partition_clk_info *clk_info; | ||
569 | int ret = 0; | ||
570 | |||
571 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
572 | |||
573 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
574 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
575 | clk = clk_info->clk_ptr; | ||
576 | if (!clk) | ||
577 | break; | ||
578 | |||
579 | if (clk_info->clk_type != RST_ONLY) { | ||
580 | if (tegra_is_clk_enabled(clk)) { | ||
581 | ret = -1; | ||
582 | break; | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | |||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | static void partition_clk_disable(int id) | ||
591 | { | ||
592 | u32 idx; | ||
593 | struct clk *clk; | ||
594 | struct partition_clk_info *clk_info; | ||
595 | |||
596 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
597 | |||
598 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
599 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
600 | clk = clk_info->clk_ptr; | ||
601 | if (!clk) | ||
602 | break; | ||
603 | |||
604 | if (clk_info->clk_type != RST_ONLY) | ||
605 | clk_disable(clk); | ||
606 | } | ||
607 | } | ||
608 | |||
609 | static void powergate_partition_assert_reset(int id) | ||
610 | { | ||
611 | u32 idx; | ||
612 | struct clk *clk_ptr; | ||
613 | struct partition_clk_info *clk_info; | ||
614 | |||
615 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
616 | |||
617 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
618 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
619 | clk_ptr = clk_info->clk_ptr; | ||
620 | if (!clk_ptr) | ||
621 | break; | ||
622 | if (clk_info->clk_type != CLK_ONLY) | ||
623 | tegra_periph_reset_assert(clk_ptr); | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static void powergate_partition_deassert_reset(int id) | ||
628 | { | ||
629 | u32 idx; | ||
630 | struct clk *clk_ptr; | ||
631 | struct partition_clk_info *clk_info; | ||
632 | |||
633 | BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); | ||
634 | |||
635 | for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) { | ||
636 | clk_info = &powergate_partition_info[id].clk_info[idx]; | ||
637 | clk_ptr = clk_info->clk_ptr; | ||
638 | if (!clk_ptr) | ||
639 | break; | ||
640 | if (clk_info->clk_type != CLK_ONLY) | ||
641 | tegra_periph_reset_deassert(clk_ptr); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | /* Must be called with clk disabled, and returns with clk disabled */ | ||
646 | static int tegra_powergate_reset_module(int id) | ||
647 | { | ||
648 | int ret; | ||
649 | |||
650 | powergate_partition_assert_reset(id); | ||
651 | |||
652 | udelay(10); | ||
653 | |||
654 | ret = partition_clk_enable(id); | ||
655 | if (ret) | ||
656 | return ret; | ||
657 | |||
658 | udelay(10); | ||
659 | |||
660 | powergate_partition_deassert_reset(id); | ||
661 | |||
662 | partition_clk_disable(id); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | /* | ||
668 | * Must be called with clk disabled, and returns with clk disabled | ||
669 | * Drivers should enable clks for partition. Unpowergates only the | ||
670 | * partition. | ||
671 | */ | ||
672 | int tegra_unpowergate_partition(int id) | ||
128 | { | 673 | { |
129 | int ret; | 674 | int ret; |
130 | 675 | ||
131 | tegra_periph_reset_assert(clk); | 676 | /* If first clk_ptr is null, fill clk info for the partition */ |
677 | if (!powergate_partition_info[id].clk_info[0].clk_ptr) | ||
678 | get_clk_info(id); | ||
132 | 679 | ||
133 | ret = tegra_powergate_power_on(id); | 680 | if (tegra_powergate_is_powered(id)) |
681 | return tegra_powergate_reset_module(id); | ||
682 | |||
683 | ret = unpowergate_module(id); | ||
134 | if (ret) | 684 | if (ret) |
135 | goto err_power; | 685 | goto err_power; |
136 | 686 | ||
137 | ret = clk_enable(clk); | 687 | powergate_partition_assert_reset(id); |
688 | |||
689 | /* Un-Powergating fails if all clks are not enabled */ | ||
690 | ret = partition_clk_enable(id); | ||
138 | if (ret) | 691 | if (ret) |
139 | goto err_clk; | 692 | goto err_clk_on; |
140 | 693 | ||
141 | udelay(10); | 694 | udelay(10); |
142 | 695 | ||
@@ -145,29 +698,130 @@ int tegra_powergate_sequence_power_up(int id, struct clk *clk) | |||
145 | goto err_clamp; | 698 | goto err_clamp; |
146 | 699 | ||
147 | udelay(10); | 700 | udelay(10); |
148 | tegra_periph_reset_deassert(clk); | 701 | powergate_partition_deassert_reset(id); |
702 | |||
703 | mc_flush_done(id); | ||
704 | |||
705 | /* Disable all clks enabled earlier. Drivers should enable clks */ | ||
706 | partition_clk_disable(id); | ||
149 | 707 | ||
150 | return 0; | 708 | return 0; |
151 | 709 | ||
152 | err_clamp: | 710 | err_clamp: |
153 | clk_disable(clk); | 711 | partition_clk_disable(id); |
154 | err_clk: | 712 | err_clk_on: |
155 | tegra_powergate_power_off(id); | 713 | powergate_module(id); |
156 | err_power: | 714 | err_power: |
715 | WARN(1, "Could not Un-Powergate %d", id); | ||
157 | return ret; | 716 | return ret; |
158 | } | 717 | } |
159 | 718 | ||
160 | #ifdef CONFIG_DEBUG_FS | 719 | /* |
720 | * Must be called with clk disabled, and returns with clk enabled | ||
721 | * Unpowergates the partition and enables all required clks. | ||
722 | */ | ||
723 | int tegra_unpowergate_partition_with_clk_on(int id) | ||
724 | { | ||
725 | int ret = 0; | ||
726 | |||
727 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
728 | /* Restrict this functions use to few partitions */ | ||
729 | BUG_ON(id != TEGRA_POWERGATE_SATA && id != TEGRA_POWERGATE_PCIE); | ||
730 | #else | ||
731 | /* Restrict this functions use to few partitions */ | ||
732 | BUG_ON(id != TEGRA_POWERGATE_PCIE); | ||
733 | #endif | ||
161 | 734 | ||
162 | static const char * const powergate_name[] = { | 735 | ret = tegra_unpowergate_partition(id); |
163 | [TEGRA_POWERGATE_CPU] = "cpu", | 736 | if (ret) |
164 | [TEGRA_POWERGATE_3D] = "3d", | 737 | goto err_unpowergating; |
165 | [TEGRA_POWERGATE_VENC] = "venc", | 738 | |
166 | [TEGRA_POWERGATE_VDEC] = "vdec", | 739 | /* Enable clks for the partition */ |
167 | [TEGRA_POWERGATE_PCIE] = "pcie", | 740 | ret = partition_clk_enable(id); |
168 | [TEGRA_POWERGATE_L2] = "l2", | 741 | if (ret) |
169 | [TEGRA_POWERGATE_MPE] = "mpe", | 742 | goto err_unpowergate_clk; |
170 | }; | 743 | |
744 | return ret; | ||
745 | |||
746 | err_unpowergate_clk: | ||
747 | tegra_powergate_partition(id); | ||
748 | WARN(1, "Could not Un-Powergate %d, err in enabling clk", id); | ||
749 | err_unpowergating: | ||
750 | WARN(1, "Could not Un-Powergate %d", id); | ||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | /* | ||
755 | * Must be called with clk disabled. Powergates the partition only | ||
756 | */ | ||
757 | int tegra_powergate_partition(int id) | ||
758 | { | ||
759 | int ret; | ||
760 | |||
761 | /* If first clk_ptr is null, fill clk info for the partition */ | ||
762 | if (powergate_partition_info[id].clk_info[0].clk_ptr) | ||
763 | get_clk_info(id); | ||
764 | powergate_partition_assert_reset(id); | ||
765 | |||
766 | /* Powergating is done only if refcnt of all clks is 0 */ | ||
767 | ret = is_partition_clk_disabled(id); | ||
768 | if (ret) | ||
769 | goto err_clk_off; | ||
770 | |||
771 | ret = powergate_module(id); | ||
772 | if (ret) | ||
773 | goto err_power_off; | ||
774 | |||
775 | return 0; | ||
776 | |||
777 | err_power_off: | ||
778 | WARN(1, "Could not Powergate Partition %d", id); | ||
779 | err_clk_off: | ||
780 | WARN(1, "Could not Powergate Partition %d, all clks not disabled", id); | ||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | int tegra_powergate_partition_with_clk_off(int id) | ||
785 | { | ||
786 | int ret = 0; | ||
787 | |||
788 | #ifndef CONFIG_ARCH_TEGRA_2x_SOC | ||
789 | /* Restrict functions use to selected partitions */ | ||
790 | BUG_ON(id != TEGRA_POWERGATE_PCIE && id != TEGRA_POWERGATE_SATA); | ||
791 | #else | ||
792 | /* Restrict functions use to selected partitions */ | ||
793 | BUG_ON(id != TEGRA_POWERGATE_PCIE); | ||
794 | #endif | ||
795 | /* Disable clks for the partition */ | ||
796 | partition_clk_disable(id); | ||
797 | |||
798 | ret = is_partition_clk_disabled(id); | ||
799 | if (ret) | ||
800 | goto err_powergate_clk; | ||
801 | |||
802 | ret = tegra_powergate_partition(id); | ||
803 | if (ret) | ||
804 | goto err_powergating; | ||
805 | |||
806 | return ret; | ||
807 | |||
808 | err_powergate_clk: | ||
809 | WARN(1, "Could not Powergate Partition %d, all clks not disabled", id); | ||
810 | err_powergating: | ||
811 | partition_clk_enable(id); | ||
812 | WARN(1, "Could not Powergate Partition %d", id); | ||
813 | return ret; | ||
814 | } | ||
815 | |||
816 | const char *tegra_powergate_get_name(int id) | ||
817 | { | ||
818 | if (id < 0 || id >= TEGRA_NUM_POWERGATE) | ||
819 | return "invalid"; | ||
820 | |||
821 | return powergate_partition_info[id].name; | ||
822 | } | ||
823 | |||
824 | #ifdef CONFIG_DEBUG_FS | ||
171 | 825 | ||
172 | static int powergate_show(struct seq_file *s, void *data) | 826 | static int powergate_show(struct seq_file *s, void *data) |
173 | { | 827 | { |
@@ -177,7 +831,7 @@ static int powergate_show(struct seq_file *s, void *data) | |||
177 | seq_printf(s, "------------------\n"); | 831 | seq_printf(s, "------------------\n"); |
178 | 832 | ||
179 | for (i = 0; i < TEGRA_NUM_POWERGATE; i++) | 833 | for (i = 0; i < TEGRA_NUM_POWERGATE; i++) |
180 | seq_printf(s, " %9s %7s\n", powergate_name[i], | 834 | seq_printf(s, " %9s %7s\n", powergate_partition_info[i].name, |
181 | tegra_powergate_is_powered(i) ? "yes" : "no"); | 835 | tegra_powergate_is_powered(i) ? "yes" : "no"); |
182 | return 0; | 836 | return 0; |
183 | } | 837 | } |
@@ -197,14 +851,13 @@ static const struct file_operations powergate_fops = { | |||
197 | static int __init powergate_debugfs_init(void) | 851 | static int __init powergate_debugfs_init(void) |
198 | { | 852 | { |
199 | struct dentry *d; | 853 | struct dentry *d; |
200 | int err = -ENOMEM; | ||
201 | 854 | ||
202 | d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, | 855 | d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, |
203 | &powergate_fops); | 856 | &powergate_fops); |
204 | if (!d) | 857 | if (!d) |
205 | return -ENOMEM; | 858 | return -ENOMEM; |
206 | 859 | ||
207 | return err; | 860 | return 0; |
208 | } | 861 | } |
209 | 862 | ||
210 | late_initcall(powergate_debugfs_init); | 863 | late_initcall(powergate_debugfs_init); |