Merge branch 'tegra/dma-reset-rework' into next/soc
[firefly-linux-kernel-4.4.55.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 static int tegra_num_powerdomains;
45 static int tegra_num_cpu_domains;
46 static const u8 *tegra_cpu_domains;
47
48 static const u8 tegra30_cpu_domains[] = {
49         TEGRA_POWERGATE_CPU,
50         TEGRA_POWERGATE_CPU1,
51         TEGRA_POWERGATE_CPU2,
52         TEGRA_POWERGATE_CPU3,
53 };
54
55 static const u8 tegra114_cpu_domains[] = {
56         TEGRA_POWERGATE_CPU0,
57         TEGRA_POWERGATE_CPU1,
58         TEGRA_POWERGATE_CPU2,
59         TEGRA_POWERGATE_CPU3,
60 };
61
62 static DEFINE_SPINLOCK(tegra_powergate_lock);
63
64 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
65
66 static u32 pmc_read(unsigned long reg)
67 {
68         return readl(pmc + reg);
69 }
70
71 static void pmc_write(u32 val, unsigned long reg)
72 {
73         writel(val, pmc + reg);
74 }
75
76 static int tegra_powergate_set(int id, bool new_state)
77 {
78         bool status;
79         unsigned long flags;
80
81         spin_lock_irqsave(&tegra_powergate_lock, flags);
82
83         status = pmc_read(PWRGATE_STATUS) & (1 << id);
84
85         if (status == new_state) {
86                 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
87                 return 0;
88         }
89
90         pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
91
92         spin_unlock_irqrestore(&tegra_powergate_lock, flags);
93
94         return 0;
95 }
96
97 int tegra_powergate_power_on(int id)
98 {
99         if (id < 0 || id >= tegra_num_powerdomains)
100                 return -EINVAL;
101
102         return tegra_powergate_set(id, true);
103 }
104
105 int tegra_powergate_power_off(int id)
106 {
107         if (id < 0 || id >= tegra_num_powerdomains)
108                 return -EINVAL;
109
110         return tegra_powergate_set(id, false);
111 }
112
113 int tegra_powergate_is_powered(int id)
114 {
115         u32 status;
116
117         if (id < 0 || id >= tegra_num_powerdomains)
118                 return -EINVAL;
119
120         status = pmc_read(PWRGATE_STATUS) & (1 << id);
121         return !!status;
122 }
123
124 int tegra_powergate_remove_clamping(int id)
125 {
126         u32 mask;
127
128         if (id < 0 || id >= tegra_num_powerdomains)
129                 return -EINVAL;
130
131         /*
132          * Tegra 2 has a bug where PCIE and VDE clamping masks are
133          * swapped relatively to the partition ids
134          */
135         if (id ==  TEGRA_POWERGATE_VDEC)
136                 mask = (1 << TEGRA_POWERGATE_PCIE);
137         else if (id == TEGRA_POWERGATE_PCIE)
138                 mask = (1 << TEGRA_POWERGATE_VDEC);
139         else
140                 mask = (1 << id);
141
142         pmc_write(mask, REMOVE_CLAMPING);
143
144         return 0;
145 }
146
147 /* Must be called with clk disabled, and returns with clk enabled */
148 int tegra_powergate_sequence_power_up(int id, struct clk *clk,
149                                         struct reset_control *rst)
150 {
151         int ret;
152
153         reset_control_assert(rst);
154
155         ret = tegra_powergate_power_on(id);
156         if (ret)
157                 goto err_power;
158
159         ret = clk_prepare_enable(clk);
160         if (ret)
161                 goto err_clk;
162
163         udelay(10);
164
165         ret = tegra_powergate_remove_clamping(id);
166         if (ret)
167                 goto err_clamp;
168
169         udelay(10);
170         reset_control_deassert(rst);
171
172         return 0;
173
174 err_clamp:
175         clk_disable_unprepare(clk);
176 err_clk:
177         tegra_powergate_power_off(id);
178 err_power:
179         return ret;
180 }
181 EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
182
183 int tegra_cpu_powergate_id(int cpuid)
184 {
185         if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
186                 return tegra_cpu_domains[cpuid];
187
188         return -EINVAL;
189 }
190
191 int __init tegra_powergate_init(void)
192 {
193         switch (tegra_chip_id) {
194         case TEGRA20:
195                 tegra_num_powerdomains = 7;
196                 break;
197         case TEGRA30:
198                 tegra_num_powerdomains = 14;
199                 tegra_num_cpu_domains = 4;
200                 tegra_cpu_domains = tegra30_cpu_domains;
201                 break;
202         case TEGRA114:
203                 tegra_num_powerdomains = 23;
204                 tegra_num_cpu_domains = 4;
205                 tegra_cpu_domains = tegra114_cpu_domains;
206                 break;
207         default:
208                 /* Unknown Tegra variant. Disable powergating */
209                 tegra_num_powerdomains = 0;
210                 break;
211         }
212
213         return 0;
214 }
215
216 #ifdef CONFIG_DEBUG_FS
217
218 static const char * const *powergate_name;
219
220 static const char * const powergate_name_t20[] = {
221         [TEGRA_POWERGATE_CPU]   = "cpu",
222         [TEGRA_POWERGATE_3D]    = "3d",
223         [TEGRA_POWERGATE_VENC]  = "venc",
224         [TEGRA_POWERGATE_VDEC]  = "vdec",
225         [TEGRA_POWERGATE_PCIE]  = "pcie",
226         [TEGRA_POWERGATE_L2]    = "l2",
227         [TEGRA_POWERGATE_MPE]   = "mpe",
228 };
229
230 static const char * const powergate_name_t30[] = {
231         [TEGRA_POWERGATE_CPU]   = "cpu0",
232         [TEGRA_POWERGATE_3D]    = "3d0",
233         [TEGRA_POWERGATE_VENC]  = "venc",
234         [TEGRA_POWERGATE_VDEC]  = "vdec",
235         [TEGRA_POWERGATE_PCIE]  = "pcie",
236         [TEGRA_POWERGATE_L2]    = "l2",
237         [TEGRA_POWERGATE_MPE]   = "mpe",
238         [TEGRA_POWERGATE_HEG]   = "heg",
239         [TEGRA_POWERGATE_SATA]  = "sata",
240         [TEGRA_POWERGATE_CPU1]  = "cpu1",
241         [TEGRA_POWERGATE_CPU2]  = "cpu2",
242         [TEGRA_POWERGATE_CPU3]  = "cpu3",
243         [TEGRA_POWERGATE_CELP]  = "celp",
244         [TEGRA_POWERGATE_3D1]   = "3d1",
245 };
246
247 static const char * const powergate_name_t114[] = {
248         [TEGRA_POWERGATE_CPU]   = "cpu0",
249         [TEGRA_POWERGATE_3D]    = "3d",
250         [TEGRA_POWERGATE_VENC]  = "venc",
251         [TEGRA_POWERGATE_VDEC]  = "vdec",
252         [TEGRA_POWERGATE_MPE]   = "mpe",
253         [TEGRA_POWERGATE_HEG]   = "heg",
254         [TEGRA_POWERGATE_CPU1]  = "cpu1",
255         [TEGRA_POWERGATE_CPU2]  = "cpu2",
256         [TEGRA_POWERGATE_CPU3]  = "cpu3",
257         [TEGRA_POWERGATE_CELP]  = "celp",
258         [TEGRA_POWERGATE_CPU0]  = "cpu0",
259         [TEGRA_POWERGATE_C0NC]  = "c0nc",
260         [TEGRA_POWERGATE_C1NC]  = "c1nc",
261         [TEGRA_POWERGATE_DIS]   = "dis",
262         [TEGRA_POWERGATE_DISB]  = "disb",
263         [TEGRA_POWERGATE_XUSBA] = "xusba",
264         [TEGRA_POWERGATE_XUSBB] = "xusbb",
265         [TEGRA_POWERGATE_XUSBC] = "xusbc",
266 };
267
268 static int powergate_show(struct seq_file *s, void *data)
269 {
270         int i;
271
272         seq_printf(s, " powergate powered\n");
273         seq_printf(s, "------------------\n");
274
275         for (i = 0; i < tegra_num_powerdomains; i++) {
276                 if (!powergate_name[i])
277                         continue;
278
279                 seq_printf(s, " %9s %7s\n", powergate_name[i],
280                         tegra_powergate_is_powered(i) ? "yes" : "no");
281         }
282
283         return 0;
284 }
285
286 static int powergate_open(struct inode *inode, struct file *file)
287 {
288         return single_open(file, powergate_show, inode->i_private);
289 }
290
291 static const struct file_operations powergate_fops = {
292         .open           = powergate_open,
293         .read           = seq_read,
294         .llseek         = seq_lseek,
295         .release        = single_release,
296 };
297
298 int __init tegra_powergate_debugfs_init(void)
299 {
300         struct dentry *d;
301
302         switch (tegra_chip_id) {
303         case TEGRA20:
304                 powergate_name = powergate_name_t20;
305                 break;
306         case TEGRA30:
307                 powergate_name = powergate_name_t30;
308                 break;
309         case TEGRA114:
310                 powergate_name = powergate_name_t114;
311                 break;
312         }
313
314         if (powergate_name) {
315                 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
316                         &powergate_fops);
317                 if (!d)
318                         return -ENOMEM;
319         }
320
321         return 0;
322 }
323
324 #endif