ARM: tegra: power: Fix warnings when hot reset is not configured
[linux-3.10.git] / arch / arm / mach-tegra / powergate.c
1 /*
2  * drivers/powergate/tegra-powergate.c
3  *
4  * Copyright (c) 2010 Google, Inc
5  *
6  * Author:
7  *      Colin Cross <ccross@google.com>
8  *
9  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <linux/kernel.h>
21 #include <linux/clk.h>
22 #include <linux/debugfs.h>
23 #include <linux/delay.h>
24 #include <linux/err.h>
25 #include <linux/init.h>
26 #include <linux/io.h>
27 #include <linux/seq_file.h>
28 #include <linux/spinlock.h>
29 #include <linux/clk/tegra.h>
30
31 #include <mach/powergate.h>
32
33 #include "fuse.h"
34 #include "iomap.h"
35
36 #define PWRGATE_TOGGLE          0x30
37 #define  PWRGATE_TOGGLE_START   (1 << 8)
38
39 #define REMOVE_CLAMPING         0x34
40
41 #define PWRGATE_STATUS          0x38
42
43 static int tegra_num_powerdomains;
44 static int tegra_num_cpu_domains;
45 static u8 *tegra_cpu_domains;
46 static u8 tegra30_cpu_domains[] = {
47         TEGRA_POWERGATE_CPU0,
48         TEGRA_POWERGATE_CPU1,
49         TEGRA_POWERGATE_CPU2,
50         TEGRA_POWERGATE_CPU3,
51 };
52
53 #define MC_CLIENT_HOTRESET_CTRL 0x200
54 #define MC_CLIENT_HOTRESET_STAT 0x204
55
56 typedef enum {
57         MC_CLIENT_AFI   = 0,
58         MC_CLIENT_AVPC  = 1,
59         MC_CLIENT_DC    = 2,
60         MC_CLIENT_DCB   = 3,
61         MC_CLIENT_EPP   = 4,
62         MC_CLIENT_G2    = 5,
63         MC_CLIENT_HC    = 6,
64         MC_CLIENT_HDA   = 7,
65         MC_CLIENT_ISP   = 8,
66         MC_CLIENT_MPCORE        = 9,
67         MC_CLIENT_MPCORELP      = 10,
68         MC_CLIENT_MPE   = 11,
69         MC_CLIENT_NV    = 12,
70         MC_CLIENT_NV2   = 13,
71         MC_CLIENT_PPCS  = 14,
72         MC_CLIENT_SATA  = 15,
73         MC_CLIENT_VDE   = 16,
74         MC_CLIENT_VI    = 17,
75         MC_CLIENT_LAST  = -1,
76 } MC_CLIENT;
77
78 static DEFINE_SPINLOCK(tegra_powergate_lock);
79
80 #define MAX_HOTRESET_CLIENT_NUM         3
81
82 typedef struct {
83         const char * name;
84         MC_CLIENT hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
85         /* add clocks for each partition*/
86 } powergate_partition;
87
88 static powergate_partition powergate_partition_info[] = {
89         [TEGRA_POWERGATE_CPU]   = { "cpu0",     {MC_CLIENT_LAST} },
90         [TEGRA_POWERGATE_L2]    = { "l2",       {MC_CLIENT_LAST} },
91         [TEGRA_POWERGATE_3D]    = { "3d0",
92                                                 {MC_CLIENT_NV, MC_CLIENT_LAST} },
93         [TEGRA_POWERGATE_PCIE]  = { "pcie",
94                                                 {MC_CLIENT_AFI, MC_CLIENT_LAST} },
95         [TEGRA_POWERGATE_VDEC]  = { "vde",
96                                                 {MC_CLIENT_VDE, MC_CLIENT_LAST} },
97         [TEGRA_POWERGATE_MPE]   = { "mpe",
98                                                 {MC_CLIENT_MPE, MC_CLIENT_LAST} },
99         [TEGRA_POWERGATE_VENC]  = { "ve",
100                                                 {MC_CLIENT_ISP, MC_CLIENT_VI, MC_CLIENT_LAST} },
101         [TEGRA_POWERGATE_CPU1]  = { "cpu1",     {MC_CLIENT_LAST}},
102         [TEGRA_POWERGATE_CPU2]  = { "cpu2",     {MC_CLIENT_LAST}},
103         [TEGRA_POWERGATE_CPU3]  = { "cpu3",     {MC_CLIENT_LAST}},
104         [TEGRA_POWERGATE_A9LP]  = { "a9lp",     {MC_CLIENT_LAST}},
105         [TEGRA_POWERGATE_SATA]  = { "sata",
106                                                 {MC_CLIENT_SATA, MC_CLIENT_LAST} },
107         [TEGRA_POWERGATE_3D1]   = { "3d1",
108                                                 {MC_CLIENT_NV2, MC_CLIENT_LAST} },
109         [TEGRA_POWERGATE_HEG]   = { "heg",
110                                                 {MC_CLIENT_G2, MC_CLIENT_EPP, MC_CLIENT_HC} },
111 };
112
113 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
114
115 static u32 pmc_read(unsigned long reg)
116 {
117         return readl(pmc + reg);
118 }
119
120 static void pmc_write(u32 val, unsigned long reg)
121 {
122         writel(val, pmc + reg);
123 }
124
125 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
126 static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
127
128 static u32 mc_read(unsigned long reg)
129 {
130         return readl(mc + reg);
131 }
132
133 static void mc_write(u32 val, unsigned long reg)
134 {
135         writel(val, mc + reg);
136 }
137
138 static void mc_flush(int id)
139 {
140         u32 idx, rst_ctrl, rst_stat;
141         MC_CLIENT mcClientBit;
142         unsigned long flags;
143
144         BUG_ON(id < 0 || id >= tegra_num_powerdomains);
145
146         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
147                 mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
148                 if (mcClientBit == MC_CLIENT_LAST)
149                         break;
150
151                 spin_lock_irqsave(&tegra_powergate_lock, flags);
152
153                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
154                 rst_ctrl |= (1 << mcClientBit);
155                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
156
157                 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
158
159                 do {
160                         udelay(10);
161                         rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
162                 } while (!(rst_stat & (1 << mcClientBit)));
163         }
164 }
165
166 static void mc_flush_done(int id)
167 {
168         u32 idx, rst_ctrl;
169         MC_CLIENT mcClientBit;
170         unsigned long flags;
171
172         BUG_ON(id < 0 || id >= tegra_num_powerdomains);
173
174         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
175                 mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
176                 if (mcClientBit == MC_CLIENT_LAST)
177                         break;
178
179                 spin_lock_irqsave(&tegra_powergate_lock, flags);
180
181                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
182                 rst_ctrl &= ~(1 << mcClientBit);
183                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
184
185                 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
186         }
187
188         wmb();
189 }
190 #else
191 static void mc_flush(int id) {}
192 static void mc_flush_done(int id) {}
193 #endif
194
195 static int tegra_powergate_set(int id, bool new_state)
196 {
197         bool status;
198         unsigned long flags;
199
200         spin_lock_irqsave(&tegra_powergate_lock, flags);
201
202         status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
203
204         if (status == new_state) {
205                 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
206                 return -EINVAL;
207         }
208
209         pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
210
211         spin_unlock_irqrestore(&tegra_powergate_lock, flags);
212
213         return 0;
214 }
215
216 int tegra_powergate_power_on(int id)
217 {
218         if (id < 0 || id >= tegra_num_powerdomains)
219                 return -EINVAL;
220
221         return tegra_powergate_set(id, true);
222 }
223
224 int tegra_powergate_power_off(int id)
225 {
226         if (id < 0 || id >= tegra_num_powerdomains)
227                 return -EINVAL;
228
229         mc_flush(id);
230
231         return tegra_powergate_set(id, false);
232 }
233
234 int tegra_powergate_is_powered(int id)
235 {
236         u32 status;
237
238         if (id < 0 || id >= tegra_num_powerdomains)
239                 return -EINVAL;
240
241         status = pmc_read(PWRGATE_STATUS) & (1 << id);
242         return !!status;
243 }
244
245 int tegra_powergate_remove_clamping(int id)
246 {
247         u32 mask;
248
249         if (id < 0 || id >= tegra_num_powerdomains)
250                 return -EINVAL;
251
252         /*
253          * PCIE and VDE clamping masks are swapped with respect to their
254          * partition ids
255          */
256         if (id ==  TEGRA_POWERGATE_VDEC)
257                 mask = (1 << TEGRA_POWERGATE_PCIE);
258         else if (id == TEGRA_POWERGATE_PCIE)
259                 mask = (1 << TEGRA_POWERGATE_VDEC);
260         else
261                 mask = (1 << id);
262
263         pmc_write(mask, REMOVE_CLAMPING);
264
265         return 0;
266 }
267
268 /* Must be called with clk disabled, and returns with clk enabled */
269 static int tegra_powergate_reset_module(struct clk *clk)
270 {
271         int ret;
272
273         tegra_periph_reset_assert(clk);
274
275         udelay(10);
276
277         ret = clk_enable(clk);
278         if (ret)
279                 return ret;
280
281         udelay(10);
282
283         tegra_periph_reset_deassert(clk);
284
285         return 0;
286 }
287
288 /* Must be called with clk disabled, and returns with clk enabled */
289 int tegra_powergate_sequence_power_up(int id, struct clk *clk)
290 {
291         int ret;
292
293         if (tegra_powergate_is_powered(id))
294                 return tegra_powergate_reset_module(clk);
295
296         tegra_periph_reset_assert(clk);
297
298         ret = tegra_powergate_power_on(id);
299         if (ret)
300                 goto err_power;
301
302         ret = clk_prepare_enable(clk);
303         if (ret)
304                 goto err_clk;
305
306         udelay(10);
307
308         ret = tegra_powergate_remove_clamping(id);
309         if (ret)
310                 goto err_clamp;
311
312         udelay(10);
313         tegra_periph_reset_deassert(clk);
314
315         mc_flush_done(id);
316
317         return 0;
318
319 err_clamp:
320         clk_disable_unprepare(clk);
321 err_clk:
322         tegra_powergate_power_off(id);
323 err_power:
324         return ret;
325 }
326
327 int tegra_cpu_powergate_id(int cpuid)
328 {
329         if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
330                 return tegra_cpu_domains[cpuid];
331
332         return -EINVAL;
333 }
334
335 int __init tegra_powergate_init(void)
336 {
337         switch (tegra_chip_id) {
338         case TEGRA20:
339                 tegra_num_powerdomains = 7;
340                 break;
341         case TEGRA30:
342                 tegra_num_powerdomains = 14;
343                 tegra_num_cpu_domains = 4;
344                 tegra_cpu_domains = tegra30_cpu_domains;
345                 break;
346         default:
347                 /* Unknown Tegra variant. Disable powergating */
348                 tegra_num_powerdomains = 0;
349                 break;
350         }
351
352         return 0;
353 }
354
355 const char* tegra_powergate_get_name(int id)
356 {
357         if (id < 0 || id >= tegra_num_powerdomains)
358                 return "invalid";
359
360         return powergate_partition_info[id].name;
361 }
362
363 #ifdef CONFIG_DEBUG_FS
364
365 static int powergate_show(struct seq_file *s, void *data)
366 {
367         int i;
368
369         seq_printf(s, " powergate powered\n");
370         seq_printf(s, "------------------\n");
371
372         for (i = 0; i < tegra_num_powerdomains; i++)
373                 seq_printf(s, " %9s %7s\n", powergate_partition_info[i].name,
374                         tegra_powergate_is_powered(i) ? "yes" : "no");
375         return 0;
376 }
377
378 static int powergate_open(struct inode *inode, struct file *file)
379 {
380         return single_open(file, powergate_show, inode->i_private);
381 }
382
383 static const struct file_operations powergate_fops = {
384         .open           = powergate_open,
385         .read           = seq_read,
386         .llseek         = seq_lseek,
387         .release        = single_release,
388 };
389
390 int __init tegra_powergate_debugfs_init(void)
391 {
392         struct dentry *d;
393
394         if (powergate_name) {
395                 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
396                         &powergate_fops);
397                 if (!d)
398                         return -ENOMEM;
399         }
400
401         return 0;
402 }
403
404 #endif