0026fb6c984b96d490e45a2937934b27a2712300
[platform/adaptation/renesas_rcar/renesas_kernel.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/export.h>
26 #include <linux/init.h>
27 #include <linux/io.h>
28 #include <linux/reset.h>
29 #include <linux/seq_file.h>
30 #include <linux/spinlock.h>
31 #include <linux/clk/tegra.h>
32 #include <linux/tegra-powergate.h>
33
34 #include "fuse.h"
35 #include "iomap.h"
36
37 #define PWRGATE_TOGGLE          0x30
38 #define  PWRGATE_TOGGLE_START   (1 << 8)
39
40 #define REMOVE_CLAMPING         0x34
41
42 #define PWRGATE_STATUS          0x38
43
44 #define GPU_RG_CNTRL            0x2d4
45
46 static int tegra_num_powerdomains;
47 static int tegra_num_cpu_domains;
48 static const u8 *tegra_cpu_domains;
49
50 static const u8 tegra30_cpu_domains[] = {
51         TEGRA_POWERGATE_CPU,
52         TEGRA_POWERGATE_CPU1,
53         TEGRA_POWERGATE_CPU2,
54         TEGRA_POWERGATE_CPU3,
55 };
56
57 static const u8 tegra114_cpu_domains[] = {
58         TEGRA_POWERGATE_CPU0,
59         TEGRA_POWERGATE_CPU1,
60         TEGRA_POWERGATE_CPU2,
61         TEGRA_POWERGATE_CPU3,
62 };
63
64 static const u8 tegra124_cpu_domains[] = {
65         TEGRA_POWERGATE_CPU0,
66         TEGRA_POWERGATE_CPU1,
67         TEGRA_POWERGATE_CPU2,
68         TEGRA_POWERGATE_CPU3,
69 };
70
71 static DEFINE_SPINLOCK(tegra_powergate_lock);
72
73 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
74
75 static u32 pmc_read(unsigned long reg)
76 {
77         return readl(pmc + reg);
78 }
79
80 static void pmc_write(u32 val, unsigned long reg)
81 {
82         writel(val, pmc + reg);
83 }
84
85 static int tegra_powergate_set(int id, bool new_state)
86 {
87         bool status;
88         unsigned long flags;
89
90         spin_lock_irqsave(&tegra_powergate_lock, flags);
91
92         status = pmc_read(PWRGATE_STATUS) & (1 << id);
93
94         if (status == new_state) {
95                 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
96                 return 0;
97         }
98
99         pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
100
101         spin_unlock_irqrestore(&tegra_powergate_lock, flags);
102
103         return 0;
104 }
105
106 int tegra_powergate_power_on(int id)
107 {
108         if (id < 0 || id >= tegra_num_powerdomains)
109                 return -EINVAL;
110
111         return tegra_powergate_set(id, true);
112 }
113
114 int tegra_powergate_power_off(int id)
115 {
116         if (id < 0 || id >= tegra_num_powerdomains)
117                 return -EINVAL;
118
119         return tegra_powergate_set(id, false);
120 }
121 EXPORT_SYMBOL(tegra_powergate_power_off);
122
123 int tegra_powergate_is_powered(int id)
124 {
125         u32 status;
126
127         if (id < 0 || id >= tegra_num_powerdomains)
128                 return -EINVAL;
129
130         status = pmc_read(PWRGATE_STATUS) & (1 << id);
131         return !!status;
132 }
133
134 int tegra_powergate_remove_clamping(int id)
135 {
136         u32 mask;
137
138         if (id < 0 || id >= tegra_num_powerdomains)
139                 return -EINVAL;
140
141         /*
142          * The Tegra124 GPU has a separate register (with different semantics)
143          * to remove clamps.
144          */
145         if (tegra_chip_id == TEGRA124) {
146                 if (id == TEGRA_POWERGATE_3D) {
147                         pmc_write(0, GPU_RG_CNTRL);
148                         return 0;
149                 }
150         }
151
152         /*
153          * Tegra 2 has a bug where PCIE and VDE clamping masks are
154          * swapped relatively to the partition ids
155          */
156         if (id == TEGRA_POWERGATE_VDEC)
157                 mask = (1 << TEGRA_POWERGATE_PCIE);
158         else if (id == TEGRA_POWERGATE_PCIE)
159                 mask = (1 << TEGRA_POWERGATE_VDEC);
160         else
161                 mask = (1 << id);
162
163         pmc_write(mask, REMOVE_CLAMPING);
164
165         return 0;
166 }
167 EXPORT_SYMBOL(tegra_powergate_remove_clamping);
168
169 /* Must be called with clk disabled, and returns with clk enabled */
170 int tegra_powergate_sequence_power_up(int id, struct clk *clk,
171                                         struct reset_control *rst)
172 {
173         int ret;
174
175         reset_control_assert(rst);
176
177         ret = tegra_powergate_power_on(id);
178         if (ret)
179                 goto err_power;
180
181         ret = clk_prepare_enable(clk);
182         if (ret)
183                 goto err_clk;
184
185         udelay(10);
186
187         ret = tegra_powergate_remove_clamping(id);
188         if (ret)
189                 goto err_clamp;
190
191         udelay(10);
192         reset_control_deassert(rst);
193
194         return 0;
195
196 err_clamp:
197         clk_disable_unprepare(clk);
198 err_clk:
199         tegra_powergate_power_off(id);
200 err_power:
201         return ret;
202 }
203 EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
204
205 int tegra_cpu_powergate_id(int cpuid)
206 {
207         if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
208                 return tegra_cpu_domains[cpuid];
209
210         return -EINVAL;
211 }
212
213 int __init tegra_powergate_init(void)
214 {
215         switch (tegra_chip_id) {
216         case TEGRA20:
217                 tegra_num_powerdomains = 7;
218                 break;
219         case TEGRA30:
220                 tegra_num_powerdomains = 14;
221                 tegra_num_cpu_domains = 4;
222                 tegra_cpu_domains = tegra30_cpu_domains;
223                 break;
224         case TEGRA114:
225                 tegra_num_powerdomains = 23;
226                 tegra_num_cpu_domains = 4;
227                 tegra_cpu_domains = tegra114_cpu_domains;
228                 break;
229         case TEGRA124:
230                 tegra_num_powerdomains = 25;
231                 tegra_num_cpu_domains = 4;
232                 tegra_cpu_domains = tegra124_cpu_domains;
233                 break;
234         default:
235                 /* Unknown Tegra variant. Disable powergating */
236                 tegra_num_powerdomains = 0;
237                 break;
238         }
239
240         return 0;
241 }
242
243 #ifdef CONFIG_DEBUG_FS
244
245 static const char * const *powergate_name;
246
247 static const char * const powergate_name_t20[] = {
248         [TEGRA_POWERGATE_CPU]   = "cpu",
249         [TEGRA_POWERGATE_3D]    = "3d",
250         [TEGRA_POWERGATE_VENC]  = "venc",
251         [TEGRA_POWERGATE_VDEC]  = "vdec",
252         [TEGRA_POWERGATE_PCIE]  = "pcie",
253         [TEGRA_POWERGATE_L2]    = "l2",
254         [TEGRA_POWERGATE_MPE]   = "mpe",
255 };
256
257 static const char * const powergate_name_t30[] = {
258         [TEGRA_POWERGATE_CPU]   = "cpu0",
259         [TEGRA_POWERGATE_3D]    = "3d0",
260         [TEGRA_POWERGATE_VENC]  = "venc",
261         [TEGRA_POWERGATE_VDEC]  = "vdec",
262         [TEGRA_POWERGATE_PCIE]  = "pcie",
263         [TEGRA_POWERGATE_L2]    = "l2",
264         [TEGRA_POWERGATE_MPE]   = "mpe",
265         [TEGRA_POWERGATE_HEG]   = "heg",
266         [TEGRA_POWERGATE_SATA]  = "sata",
267         [TEGRA_POWERGATE_CPU1]  = "cpu1",
268         [TEGRA_POWERGATE_CPU2]  = "cpu2",
269         [TEGRA_POWERGATE_CPU3]  = "cpu3",
270         [TEGRA_POWERGATE_CELP]  = "celp",
271         [TEGRA_POWERGATE_3D1]   = "3d1",
272 };
273
274 static const char * const powergate_name_t114[] = {
275         [TEGRA_POWERGATE_CPU]   = "crail",
276         [TEGRA_POWERGATE_3D]    = "3d",
277         [TEGRA_POWERGATE_VENC]  = "venc",
278         [TEGRA_POWERGATE_VDEC]  = "vdec",
279         [TEGRA_POWERGATE_MPE]   = "mpe",
280         [TEGRA_POWERGATE_HEG]   = "heg",
281         [TEGRA_POWERGATE_CPU1]  = "cpu1",
282         [TEGRA_POWERGATE_CPU2]  = "cpu2",
283         [TEGRA_POWERGATE_CPU3]  = "cpu3",
284         [TEGRA_POWERGATE_CELP]  = "celp",
285         [TEGRA_POWERGATE_CPU0]  = "cpu0",
286         [TEGRA_POWERGATE_C0NC]  = "c0nc",
287         [TEGRA_POWERGATE_C1NC]  = "c1nc",
288         [TEGRA_POWERGATE_DIS]   = "dis",
289         [TEGRA_POWERGATE_DISB]  = "disb",
290         [TEGRA_POWERGATE_XUSBA] = "xusba",
291         [TEGRA_POWERGATE_XUSBB] = "xusbb",
292         [TEGRA_POWERGATE_XUSBC] = "xusbc",
293 };
294
295 static const char * const powergate_name_t124[] = {
296         [TEGRA_POWERGATE_CPU]   = "crail",
297         [TEGRA_POWERGATE_3D]    = "3d",
298         [TEGRA_POWERGATE_VENC]  = "venc",
299         [TEGRA_POWERGATE_PCIE]  = "pcie",
300         [TEGRA_POWERGATE_VDEC]  = "vdec",
301         [TEGRA_POWERGATE_L2]    = "l2",
302         [TEGRA_POWERGATE_MPE]   = "mpe",
303         [TEGRA_POWERGATE_HEG]   = "heg",
304         [TEGRA_POWERGATE_SATA]  = "sata",
305         [TEGRA_POWERGATE_CPU1]  = "cpu1",
306         [TEGRA_POWERGATE_CPU2]  = "cpu2",
307         [TEGRA_POWERGATE_CPU3]  = "cpu3",
308         [TEGRA_POWERGATE_CELP]  = "celp",
309         [TEGRA_POWERGATE_CPU0]  = "cpu0",
310         [TEGRA_POWERGATE_C0NC]  = "c0nc",
311         [TEGRA_POWERGATE_C1NC]  = "c1nc",
312         [TEGRA_POWERGATE_SOR]   = "sor",
313         [TEGRA_POWERGATE_DIS]   = "dis",
314         [TEGRA_POWERGATE_DISB]  = "disb",
315         [TEGRA_POWERGATE_XUSBA] = "xusba",
316         [TEGRA_POWERGATE_XUSBB] = "xusbb",
317         [TEGRA_POWERGATE_XUSBC] = "xusbc",
318         [TEGRA_POWERGATE_VIC]   = "vic",
319         [TEGRA_POWERGATE_IRAM]  = "iram",
320 };
321
322 static int powergate_show(struct seq_file *s, void *data)
323 {
324         int i;
325
326         seq_printf(s, " powergate powered\n");
327         seq_printf(s, "------------------\n");
328
329         for (i = 0; i < tegra_num_powerdomains; i++) {
330                 if (!powergate_name[i])
331                         continue;
332
333                 seq_printf(s, " %9s %7s\n", powergate_name[i],
334                         tegra_powergate_is_powered(i) ? "yes" : "no");
335         }
336
337         return 0;
338 }
339
340 static int powergate_open(struct inode *inode, struct file *file)
341 {
342         return single_open(file, powergate_show, inode->i_private);
343 }
344
345 static const struct file_operations powergate_fops = {
346         .open           = powergate_open,
347         .read           = seq_read,
348         .llseek         = seq_lseek,
349         .release        = single_release,
350 };
351
352 int __init tegra_powergate_debugfs_init(void)
353 {
354         struct dentry *d;
355
356         switch (tegra_chip_id) {
357         case TEGRA20:
358                 powergate_name = powergate_name_t20;
359                 break;
360         case TEGRA30:
361                 powergate_name = powergate_name_t30;
362                 break;
363         case TEGRA114:
364                 powergate_name = powergate_name_t114;
365                 break;
366         case TEGRA124:
367                 powergate_name = powergate_name_t124;
368                 break;
369         }
370
371         if (powergate_name) {
372                 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
373                         &powergate_fops);
374                 if (!d)
375                         return -ENOMEM;
376         }
377
378         return 0;
379 }
380
381 #endif