d78c7306638e6f0bb32168ccaa38ca353b9491a1
[linux-3.10.git] / arch / arm / mach-tegra / powergate-t12x.c
1 /*
2  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14
15 #include <linux/spinlock.h>
16 #include <linux/delay.h>
17
18 #include <asm/atomic.h>
19
20 #include <mach/powergate.h>
21
22 #include "powergate-priv.h"
23 #include "powergate-ops-txx.h"
24 #include "powergate-ops-t1xx.h"
25
26 enum mc_client {
27         MC_CLIENT_AFI           = 0,
28         MC_CLIENT_DC            = 2,
29         MC_CLIENT_DCB           = 3,
30         MC_CLIENT_ISP           = 8,
31         MC_CLIENT_MSENC         = 11,
32         MC_CLIENT_SATA          = 15,
33         MC_CLIENT_VDE           = 16,
34         MC_CLIENT_VI            = 17,
35         MC_CLIENT_VIC           = 18,
36         MC_CLIENT_XUSB_HOST     = 19,
37         MC_CLIENT_XUSB_DEV      = 20,
38         MC_CLIENT_LAST          = -1,
39 };
40
41 struct tegra12x_powergate_mc_client_info {
42         enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
43 };
44
45 static struct tegra12x_powergate_mc_client_info tegra12x_pg_mc_info[] = {
46         [TEGRA_POWERGATE_VDEC] = {
47                 .hot_reset_clients = {
48                         [0] = MC_CLIENT_VDE,
49                         [1] = MC_CLIENT_LAST,
50                 },
51         },
52         [TEGRA_POWERGATE_MPE] = {
53                 .hot_reset_clients = {
54                         [0] = MC_CLIENT_MSENC,
55                         [1] = MC_CLIENT_LAST,
56                 },
57         },
58         [TEGRA_POWERGATE_VENC] = {
59                 .hot_reset_clients = {
60                         [0] = MC_CLIENT_ISP,
61                         [1] = MC_CLIENT_VI,
62                         [2] = MC_CLIENT_LAST,
63                 },
64         },
65         [TEGRA_POWERGATE_DISA] = {
66                 .hot_reset_clients = {
67                         [0] = MC_CLIENT_DC,
68                         [1] = MC_CLIENT_LAST,
69                 },
70         },
71         [TEGRA_POWERGATE_DISB] = {
72                 .hot_reset_clients = {
73                         [0] = MC_CLIENT_DCB,
74                         [1] = MC_CLIENT_LAST,
75                 },
76         },
77         [TEGRA_POWERGATE_XUSBA] = {
78                 .hot_reset_clients = {
79                         [0] = MC_CLIENT_LAST,
80                 },
81         },
82         [TEGRA_POWERGATE_XUSBB] = {
83                 .hot_reset_clients = {
84                         [0] = MC_CLIENT_XUSB_DEV,
85                         [1] = MC_CLIENT_LAST
86                 },
87         },
88         [TEGRA_POWERGATE_XUSBC] = {
89                 .hot_reset_clients = {
90                         [0] = MC_CLIENT_XUSB_HOST,
91                         [1] = MC_CLIENT_LAST,
92                 },
93         },
94 #ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
95         [TEGRA_POWERGATE_PCIE] = {
96                 .hot_reset_clients = {
97                         [0] = MC_CLIENT_AFI,
98                         [1] = MC_CLIENT_LAST,
99                 },
100         },
101 #endif
102 #ifdef CONFIG_ARCH_TEGRA_HAS_SATA
103         [TEGRA_POWERGATE_SATA] = {
104                 .hot_reset_clients = {
105                         [0] = MC_CLIENT_SATA,
106                         [1] = MC_CLIENT_LAST,
107                 },
108         },
109 #endif
110         [TEGRA_POWERGATE_SOR] = {
111                 .hot_reset_clients = {
112                         [0] = MC_CLIENT_LAST,
113                 },
114         },
115 #ifdef CONFIG_ARCH_TEGRA_VIC
116         [TEGRA_POWERGATE_VIC] = {
117                 .hot_reset_clients = {
118                         [0] = MC_CLIENT_VIC,
119                         [1] = MC_CLIENT_LAST,
120                 },
121         },
122 #endif
123 };
124
125 static struct powergate_partition_info tegra12x_powergate_partition_info[] = {
126         [TEGRA_POWERGATE_VDEC] = {
127                 .name = "vde",
128                 .clk_info = {
129                         [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
130                 },
131         },
132         [TEGRA_POWERGATE_MPE] = {
133                 .name = "mpe",
134                 .clk_info = {
135                         [0] = { .clk_name = "msenc.cbus", .clk_type = CLK_AND_RST },
136                 },
137         },
138         [TEGRA_POWERGATE_VENC] = {
139                 .name = "ve",
140                 .clk_info = {
141                         [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
142                         [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
143                         [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
144                 },
145         },
146         [TEGRA_POWERGATE_DISA] = {
147                 .name = "disa",
148                 .clk_info = {
149                         [0] = { .clk_name = "disp1", .clk_type = CLK_AND_RST },
150                         [1] = { .clk_name = "dsia", .clk_type = CLK_AND_RST },
151                         [2] = { .clk_name = "dsib", .clk_type = CLK_AND_RST },
152                         [3] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
153                         [4] = { .clk_name = "mipi-cal", .clk_type = CLK_AND_RST },
154                 },
155         },
156         [TEGRA_POWERGATE_DISB] = {
157                 .name = "disb",
158                 .clk_info = {
159                         [0] = { .clk_name = "disp2", .clk_type = CLK_AND_RST },
160                         [1] = { .clk_name = "hdmi", .clk_type = CLK_AND_RST },
161                 },
162         },
163         [TEGRA_POWERGATE_XUSBA] = {
164                 .name = "xusba",
165                 .clk_info = {
166                         [0] = { .clk_name = "xusb_ss", .clk_type = CLK_AND_RST },
167                 },
168         },
169         [TEGRA_POWERGATE_XUSBB] = {
170                 .name = "xusbb",
171                 .clk_info = {
172                         [0] = { .clk_name = "xusb_dev", .clk_type = CLK_AND_RST },
173                 },
174         },
175         [TEGRA_POWERGATE_XUSBC] = {
176                 .name = "xusbc",
177                 .clk_info = {
178                         [0] = { .clk_name = "xusb_host", .clk_type = CLK_AND_RST },
179                 },
180         },
181 #ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
182         [TEGRA_POWERGATE_PCIE] = {
183                 .name = "pcie",
184                 .clk_info = {
185                         [0] = { .clk_name = "afi", .clk_type = CLK_AND_RST },
186                         [1] = { .clk_name = "pcie", .clk_type = CLK_AND_RST },
187                         [2] = { .clk_name = "cml0", .clk_type = CLK_ONLY },
188                         [3] = { .clk_name = "pciex", .clk_type = RST_ONLY },
189                 },
190         },
191 #endif
192 #ifdef CONFIG_ARCH_TEGRA_HAS_SATA
193         [TEGRA_POWERGATE_SATA] = {
194                 .name = "sata",
195                 .clk_info = {
196                         [0] = { .clk_name = "sata", .clk_type = CLK_AND_RST },
197                         [1] = { .clk_name = "sata_oob", .clk_type = CLK_AND_RST },
198                         [2] = { .clk_name = "cml1", .clk_type = CLK_ONLY },
199                         [3] = { .clk_name = "sata_cold", .clk_type = RST_ONLY },
200                 },
201         },
202 #endif
203         [TEGRA_POWERGATE_SOR] = {
204                 .name = "sor",
205                 .clk_info = {
206                         [0] = { .clk_name = "sor0", .clk_type = CLK_AND_RST },
207                 },
208         },
209 #ifdef CONFIG_ARCH_TEGRA_VIC
210         [TEGRA_POWERGATE_VIC] = {
211                 .name = "vic",
212                 .clk_info = {
213                         [0] = { .clk_name = "vic03.cbus", .clk_type = CLK_AND_RST },
214                 },
215         },
216 #endif
217 };
218
219 static atomic_t ref_count_a = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISA */
220 static atomic_t ref_count_b = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISB */
221
222 #define MC_CLIENT_HOTRESET_CTRL         0x200
223 #define MC_CLIENT_HOTRESET_STAT         0x204
224
225 static DEFINE_SPINLOCK(tegra12x_powergate_lock);
226
227 #define HOTRESET_READ_COUNT     5
228 static bool tegra12x_stable_hotreset_check(u32 *stat)
229 {
230         int i;
231         u32 cur_stat;
232         u32 prv_stat;
233         unsigned long flags;
234
235         spin_lock_irqsave(&tegra12x_powergate_lock, flags);
236         prv_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
237         for (i = 0; i < HOTRESET_READ_COUNT; i++) {
238                 cur_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
239                 if (cur_stat != prv_stat) {
240                         spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
241                         return false;
242                 }
243         }
244         *stat = cur_stat;
245         spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
246         return true;
247 }
248
249 int tegra12x_powergate_mc_enable(int id)
250 {
251         return 0;
252 }
253
254 int tegra12x_powergate_mc_disable(int id)
255 {
256         return 0;
257 }
258
259 int tegra12x_powergate_mc_flush(int id)
260 {
261         u32 idx, rst_ctrl, rst_stat;
262         enum mc_client mcClientBit;
263         unsigned long flags;
264         bool ret;
265
266         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
267                 mcClientBit =
268                         tegra12x_pg_mc_info[id].hot_reset_clients[idx];
269                 if (mcClientBit == MC_CLIENT_LAST)
270                         break;
271
272                 spin_lock_irqsave(&tegra12x_powergate_lock, flags);
273
274                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
275                 rst_ctrl |= (1 << mcClientBit);
276                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
277
278                 spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
279
280                 do {
281                         udelay(10);
282                         rst_stat = 0;
283                         ret = tegra12x_stable_hotreset_check(&rst_stat);
284                         if (!ret)
285                                 continue;
286                 } while (!(rst_stat & (1 << mcClientBit)));
287         }
288
289         return 0;
290 }
291
292 int tegra12x_powergate_mc_flush_done(int id)
293 {
294         u32 idx, rst_ctrl;
295         enum mc_client mcClientBit;
296         unsigned long flags;
297
298         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
299                 mcClientBit =
300                         tegra12x_pg_mc_info[id].hot_reset_clients[idx];
301                 if (mcClientBit == MC_CLIENT_LAST)
302                         break;
303
304                 spin_lock_irqsave(&tegra12x_powergate_lock, flags);
305
306                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
307                 rst_ctrl &= ~(1 << mcClientBit);
308                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
309
310                 spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
311         }
312
313         wmb();
314
315         return 0;
316 }
317
318 int tegra12x_powergate_partition(int id)
319 {
320         int ret;
321
322         WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
323         WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
324
325         if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
326                 return 0;
327         else if (id == TEGRA_POWERGATE_DISB &&
328                 atomic_dec_return(&ref_count_b) != 0)
329                 return 0;
330
331         /* call common power-gate API for t1xx */
332         ret = tegra1xx_powergate(id,
333                 &tegra12x_powergate_partition_info[id]);
334
335         return ret;
336 }
337
338 int tegra12x_unpowergate_partition(int id)
339 {
340         int ret;
341
342         WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
343         WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
344
345         if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
346                 return 0;
347         else if (id == TEGRA_POWERGATE_DISB &&
348                 atomic_inc_return(&ref_count_b) != 1)
349                 return 0;
350
351         ret = tegra1xx_unpowergate(id,
352                 &tegra12x_powergate_partition_info[id]);
353
354         return ret;
355 }
356
357 int tegra12x_powergate_partition_with_clk_off(int id)
358 {
359         return tegraxx_powergate_partition_with_clk_off(id,
360                 &tegra12x_powergate_partition_info[id]);
361 }
362
363 int tegra12x_unpowergate_partition_with_clk_on(int id)
364 {
365         return tegraxx_unpowergate_partition_with_clk_on(id,
366                 &tegra12x_powergate_partition_info[id]);
367 }
368
369 const char *tegra12x_get_powergate_domain_name(int id)
370 {
371         return tegra12x_powergate_partition_info[id].name;
372 }
373
374 spinlock_t *tegra12x_get_powergate_lock(void)
375 {
376         return &tegra12x_powergate_lock;
377 }
378
379 static struct powergate_ops tegra12x_powergate_ops = {
380         .soc_name = "tegra12x",
381
382         .num_powerdomains = TEGRA_NUM_POWERGATE,
383
384         .get_powergate_lock = tegra12x_get_powergate_lock,
385         .get_powergate_domain_name = tegra12x_get_powergate_domain_name,
386
387         .powergate_partition = tegra12x_powergate_partition,
388         .unpowergate_partition = tegra12x_unpowergate_partition,
389
390         .powergate_partition_with_clk_off =  tegra12x_powergate_partition_with_clk_off,
391         .unpowergate_partition_with_clk_on = tegra12x_unpowergate_partition_with_clk_on,
392
393         .powergate_mc_enable = tegra12x_powergate_mc_enable,
394         .powergate_mc_disable = tegra12x_powergate_mc_disable,
395
396         .powergate_mc_flush = tegra12x_powergate_mc_flush,
397         .powergate_mc_flush_done = tegra12x_powergate_mc_flush_done,
398 };
399
400 struct powergate_ops *tegra12x_powergate_init_chip_support(void)
401 {
402         return &tegra12x_powergate_ops;
403 }