Commit | Line | Data |
---|---|---|
8f8f484b PG |
1 | /* |
2 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
584ac4e9 | 17 | #include <linux/clkdev.h> |
8f8f484b PG |
18 | #include <linux/clk.h> |
19 | #include <linux/clk-provider.h> | |
4236e752 | 20 | #include <linux/delay.h> |
61fd290d PG |
21 | #include <linux/of.h> |
22 | #include <linux/clk/tegra.h> | |
6d5b988e | 23 | #include <linux/reset-controller.h> |
306a7f91 TR |
24 | |
25 | #include <soc/tegra/fuse.h> | |
8f8f484b PG |
26 | |
27 | #include "clk.h" | |
28 | ||
d5ff89a8 PDS |
29 | #define CLK_OUT_ENB_L 0x010 |
30 | #define CLK_OUT_ENB_H 0x014 | |
31 | #define CLK_OUT_ENB_U 0x018 | |
32 | #define CLK_OUT_ENB_V 0x360 | |
33 | #define CLK_OUT_ENB_W 0x364 | |
34 | #define CLK_OUT_ENB_X 0x280 | |
699b477a | 35 | #define CLK_OUT_ENB_Y 0x298 |
d5ff89a8 PDS |
36 | #define CLK_OUT_ENB_SET_L 0x320 |
37 | #define CLK_OUT_ENB_CLR_L 0x324 | |
38 | #define CLK_OUT_ENB_SET_H 0x328 | |
39 | #define CLK_OUT_ENB_CLR_H 0x32c | |
40 | #define CLK_OUT_ENB_SET_U 0x330 | |
41 | #define CLK_OUT_ENB_CLR_U 0x334 | |
42 | #define CLK_OUT_ENB_SET_V 0x440 | |
43 | #define CLK_OUT_ENB_CLR_V 0x444 | |
44 | #define CLK_OUT_ENB_SET_W 0x448 | |
45 | #define CLK_OUT_ENB_CLR_W 0x44c | |
46 | #define CLK_OUT_ENB_SET_X 0x284 | |
47 | #define CLK_OUT_ENB_CLR_X 0x288 | |
699b477a TR |
48 | #define CLK_OUT_ENB_SET_Y 0x29c |
49 | #define CLK_OUT_ENB_CLR_Y 0x2a0 | |
d5ff89a8 PDS |
50 | |
51 | #define RST_DEVICES_L 0x004 | |
52 | #define RST_DEVICES_H 0x008 | |
53 | #define RST_DEVICES_U 0x00C | |
d5ff89a8 PDS |
54 | #define RST_DEVICES_V 0x358 |
55 | #define RST_DEVICES_W 0x35C | |
56 | #define RST_DEVICES_X 0x28C | |
699b477a | 57 | #define RST_DEVICES_Y 0x2a4 |
d5ff89a8 PDS |
58 | #define RST_DEVICES_SET_L 0x300 |
59 | #define RST_DEVICES_CLR_L 0x304 | |
60 | #define RST_DEVICES_SET_H 0x308 | |
61 | #define RST_DEVICES_CLR_H 0x30c | |
62 | #define RST_DEVICES_SET_U 0x310 | |
63 | #define RST_DEVICES_CLR_U 0x314 | |
64 | #define RST_DEVICES_SET_V 0x430 | |
65 | #define RST_DEVICES_CLR_V 0x434 | |
66 | #define RST_DEVICES_SET_W 0x438 | |
67 | #define RST_DEVICES_CLR_W 0x43c | |
2b239077 PDS |
68 | #define RST_DEVICES_SET_X 0x290 |
69 | #define RST_DEVICES_CLR_X 0x294 | |
699b477a TR |
70 | #define RST_DEVICES_SET_Y 0x2a8 |
71 | #define RST_DEVICES_CLR_Y 0x2ac | |
d5ff89a8 | 72 | |
61fd290d | 73 | /* Global data of Tegra CPU CAR ops */ |
6a676fa0 PDS |
74 | static struct tegra_cpu_car_ops dummy_car_ops; |
75 | struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; | |
61fd290d | 76 | |
343a607c | 77 | int *periph_clk_enb_refcnt; |
d5ff89a8 | 78 | static int periph_banks; |
343a607c PDS |
79 | static struct clk **clks; |
80 | static int clk_num; | |
81 | static struct clk_onecell_data clk_data; | |
d5ff89a8 | 82 | |
66b6f3d0 MP |
83 | /* Handlers for SoC-specific reset lines */ |
84 | static int (*special_reset_assert)(unsigned long); | |
85 | static int (*special_reset_deassert)(unsigned long); | |
86 | static unsigned int num_special_reset; | |
87 | ||
7e14f223 | 88 | static const struct tegra_clk_periph_regs periph_regs[] = { |
d5ff89a8 PDS |
89 | [0] = { |
90 | .enb_reg = CLK_OUT_ENB_L, | |
91 | .enb_set_reg = CLK_OUT_ENB_SET_L, | |
92 | .enb_clr_reg = CLK_OUT_ENB_CLR_L, | |
93 | .rst_reg = RST_DEVICES_L, | |
94 | .rst_set_reg = RST_DEVICES_SET_L, | |
95 | .rst_clr_reg = RST_DEVICES_CLR_L, | |
96 | }, | |
97 | [1] = { | |
98 | .enb_reg = CLK_OUT_ENB_H, | |
99 | .enb_set_reg = CLK_OUT_ENB_SET_H, | |
100 | .enb_clr_reg = CLK_OUT_ENB_CLR_H, | |
101 | .rst_reg = RST_DEVICES_H, | |
102 | .rst_set_reg = RST_DEVICES_SET_H, | |
103 | .rst_clr_reg = RST_DEVICES_CLR_H, | |
104 | }, | |
105 | [2] = { | |
106 | .enb_reg = CLK_OUT_ENB_U, | |
107 | .enb_set_reg = CLK_OUT_ENB_SET_U, | |
108 | .enb_clr_reg = CLK_OUT_ENB_CLR_U, | |
109 | .rst_reg = RST_DEVICES_U, | |
110 | .rst_set_reg = RST_DEVICES_SET_U, | |
111 | .rst_clr_reg = RST_DEVICES_CLR_U, | |
112 | }, | |
113 | [3] = { | |
114 | .enb_reg = CLK_OUT_ENB_V, | |
115 | .enb_set_reg = CLK_OUT_ENB_SET_V, | |
116 | .enb_clr_reg = CLK_OUT_ENB_CLR_V, | |
117 | .rst_reg = RST_DEVICES_V, | |
118 | .rst_set_reg = RST_DEVICES_SET_V, | |
119 | .rst_clr_reg = RST_DEVICES_CLR_V, | |
120 | }, | |
121 | [4] = { | |
122 | .enb_reg = CLK_OUT_ENB_W, | |
123 | .enb_set_reg = CLK_OUT_ENB_SET_W, | |
124 | .enb_clr_reg = CLK_OUT_ENB_CLR_W, | |
125 | .rst_reg = RST_DEVICES_W, | |
126 | .rst_set_reg = RST_DEVICES_SET_W, | |
127 | .rst_clr_reg = RST_DEVICES_CLR_W, | |
128 | }, | |
2b239077 PDS |
129 | [5] = { |
130 | .enb_reg = CLK_OUT_ENB_X, | |
131 | .enb_set_reg = CLK_OUT_ENB_SET_X, | |
132 | .enb_clr_reg = CLK_OUT_ENB_CLR_X, | |
133 | .rst_reg = RST_DEVICES_X, | |
134 | .rst_set_reg = RST_DEVICES_SET_X, | |
135 | .rst_clr_reg = RST_DEVICES_CLR_X, | |
136 | }, | |
699b477a TR |
137 | [6] = { |
138 | .enb_reg = CLK_OUT_ENB_Y, | |
139 | .enb_set_reg = CLK_OUT_ENB_SET_Y, | |
140 | .enb_clr_reg = CLK_OUT_ENB_CLR_Y, | |
141 | .rst_reg = RST_DEVICES_Y, | |
142 | .rst_set_reg = RST_DEVICES_SET_Y, | |
143 | .rst_clr_reg = RST_DEVICES_CLR_Y, | |
144 | }, | |
d5ff89a8 PDS |
145 | }; |
146 | ||
6d5b988e SW |
147 | static void __iomem *clk_base; |
148 | ||
149 | static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev, | |
150 | unsigned long id) | |
151 | { | |
152 | /* | |
153 | * If peripheral is on the APB bus then we must read the APB bus to | |
154 | * flush the write operation in apb bus. This will avoid peripheral | |
155 | * access after disabling clock. Since the reset driver has no | |
156 | * knowledge of which reset IDs represent which devices, simply do | |
157 | * this all the time. | |
158 | */ | |
159 | tegra_read_chipid(); | |
160 | ||
66b6f3d0 MP |
161 | if (id < periph_banks * 32) { |
162 | writel_relaxed(BIT(id % 32), | |
163 | clk_base + periph_regs[id / 32].rst_set_reg); | |
164 | return 0; | |
165 | } else if (id < periph_banks * 32 + num_special_reset) { | |
166 | return special_reset_assert(id); | |
167 | } | |
6d5b988e | 168 | |
66b6f3d0 | 169 | return -EINVAL; |
6d5b988e SW |
170 | } |
171 | ||
172 | static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev, | |
173 | unsigned long id) | |
174 | { | |
66b6f3d0 MP |
175 | if (id < periph_banks * 32) { |
176 | writel_relaxed(BIT(id % 32), | |
177 | clk_base + periph_regs[id / 32].rst_clr_reg); | |
178 | return 0; | |
179 | } else if (id < periph_banks * 32 + num_special_reset) { | |
180 | return special_reset_deassert(id); | |
181 | } | |
6d5b988e | 182 | |
66b6f3d0 | 183 | return -EINVAL; |
6d5b988e SW |
184 | } |
185 | ||
4236e752 MP |
186 | static int tegra_clk_rst_reset(struct reset_controller_dev *rcdev, |
187 | unsigned long id) | |
188 | { | |
189 | int err; | |
190 | ||
191 | err = tegra_clk_rst_assert(rcdev, id); | |
192 | if (err) | |
193 | return err; | |
194 | ||
195 | udelay(1); | |
196 | ||
197 | return tegra_clk_rst_deassert(rcdev, id); | |
198 | } | |
199 | ||
7e14f223 | 200 | const struct tegra_clk_periph_regs *get_reg_bank(int clkid) |
d5ff89a8 PDS |
201 | { |
202 | int reg_bank = clkid / 32; | |
203 | ||
204 | if (reg_bank < periph_banks) | |
205 | return &periph_regs[reg_bank]; | |
206 | else { | |
207 | WARN_ON(1); | |
208 | return NULL; | |
209 | } | |
210 | } | |
211 | ||
6d5b988e | 212 | struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks) |
d5ff89a8 | 213 | { |
6d5b988e SW |
214 | clk_base = regs; |
215 | ||
343a607c PDS |
216 | if (WARN_ON(banks > ARRAY_SIZE(periph_regs))) |
217 | return NULL; | |
218 | ||
6396bb22 KC |
219 | periph_clk_enb_refcnt = kcalloc(32 * banks, |
220 | sizeof(*periph_clk_enb_refcnt), | |
221 | GFP_KERNEL); | |
343a607c PDS |
222 | if (!periph_clk_enb_refcnt) |
223 | return NULL; | |
d5ff89a8 | 224 | |
343a607c | 225 | periph_banks = banks; |
d5ff89a8 | 226 | |
6396bb22 | 227 | clks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL); |
343a607c PDS |
228 | if (!clks) |
229 | kfree(periph_clk_enb_refcnt); | |
230 | ||
231 | clk_num = num; | |
232 | ||
233 | return clks; | |
d5ff89a8 PDS |
234 | } |
235 | ||
8f8f484b PG |
236 | void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, |
237 | struct clk *clks[], int clk_max) | |
238 | { | |
239 | struct clk *clk; | |
240 | ||
241 | for (; dup_list->clk_id < clk_max; dup_list++) { | |
242 | clk = clks[dup_list->clk_id]; | |
243 | dup_list->lookup.clk = clk; | |
244 | clkdev_add(&dup_list->lookup); | |
245 | } | |
246 | } | |
247 | ||
248 | void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, | |
249 | struct clk *clks[], int clk_max) | |
250 | { | |
251 | struct clk *clk; | |
252 | ||
253 | for (; tbl->clk_id < clk_max; tbl++) { | |
254 | clk = clks[tbl->clk_id]; | |
b9e742c3 TV |
255 | if (IS_ERR_OR_NULL(clk)) { |
256 | pr_err("%s: invalid entry %ld in clks array for id %d\n", | |
257 | __func__, PTR_ERR(clk), tbl->clk_id); | |
258 | WARN_ON(1); | |
259 | ||
260 | continue; | |
261 | } | |
8f8f484b PG |
262 | |
263 | if (tbl->parent_id < clk_max) { | |
264 | struct clk *parent = clks[tbl->parent_id]; | |
265 | if (clk_set_parent(clk, parent)) { | |
266 | pr_err("%s: Failed to set parent %s of %s\n", | |
267 | __func__, __clk_get_name(parent), | |
268 | __clk_get_name(clk)); | |
269 | WARN_ON(1); | |
270 | } | |
271 | } | |
272 | ||
273 | if (tbl->rate) | |
274 | if (clk_set_rate(clk, tbl->rate)) { | |
275 | pr_err("%s: Failed to set rate %lu of %s\n", | |
276 | __func__, tbl->rate, | |
277 | __clk_get_name(clk)); | |
278 | WARN_ON(1); | |
279 | } | |
280 | ||
281 | if (tbl->state) | |
282 | if (clk_prepare_enable(clk)) { | |
283 | pr_err("%s: Failed to enable %s\n", __func__, | |
284 | __clk_get_name(clk)); | |
285 | WARN_ON(1); | |
286 | } | |
287 | } | |
288 | } | |
61fd290d | 289 | |
7ba256d2 | 290 | static const struct reset_control_ops rst_ops = { |
6d5b988e SW |
291 | .assert = tegra_clk_rst_assert, |
292 | .deassert = tegra_clk_rst_deassert, | |
4236e752 | 293 | .reset = tegra_clk_rst_reset, |
6d5b988e SW |
294 | }; |
295 | ||
296 | static struct reset_controller_dev rst_ctlr = { | |
297 | .ops = &rst_ops, | |
298 | .owner = THIS_MODULE, | |
299 | .of_reset_n_cells = 1, | |
300 | }; | |
301 | ||
5d797111 DO |
302 | void __init tegra_add_of_provider(struct device_node *np, |
303 | void *clk_src_onecell_get) | |
343a607c PDS |
304 | { |
305 | int i; | |
306 | ||
307 | for (i = 0; i < clk_num; i++) { | |
308 | if (IS_ERR(clks[i])) { | |
309 | pr_err | |
310 | ("Tegra clk %d: register failed with %ld\n", | |
311 | i, PTR_ERR(clks[i])); | |
312 | } | |
313 | if (!clks[i]) | |
314 | clks[i] = ERR_PTR(-EINVAL); | |
315 | } | |
316 | ||
317 | clk_data.clks = clks; | |
318 | clk_data.clk_num = clk_num; | |
5d797111 | 319 | of_clk_add_provider(np, clk_src_onecell_get, &clk_data); |
6d5b988e SW |
320 | |
321 | rst_ctlr.of_node = np; | |
66b6f3d0 | 322 | rst_ctlr.nr_resets = periph_banks * 32 + num_special_reset; |
6d5b988e | 323 | reset_controller_register(&rst_ctlr); |
343a607c PDS |
324 | } |
325 | ||
66b6f3d0 MP |
326 | void __init tegra_init_special_resets(unsigned int num, |
327 | int (*assert)(unsigned long), | |
328 | int (*deassert)(unsigned long)) | |
329 | { | |
330 | num_special_reset = num; | |
331 | special_reset_assert = assert; | |
332 | special_reset_deassert = deassert; | |
333 | } | |
334 | ||
73d37e4c PDS |
335 | void __init tegra_register_devclks(struct tegra_devclk *dev_clks, int num) |
336 | { | |
337 | int i; | |
338 | ||
339 | for (i = 0; i < num; i++, dev_clks++) | |
340 | clk_register_clkdev(clks[dev_clks->dt_id], dev_clks->con_id, | |
341 | dev_clks->dev_id); | |
9f0030c8 PDS |
342 | |
343 | for (i = 0; i < clk_num; i++) { | |
344 | if (!IS_ERR_OR_NULL(clks[i])) | |
345 | clk_register_clkdev(clks[i], __clk_get_name(clks[i]), | |
346 | "tegra-clk-debug"); | |
347 | } | |
73d37e4c PDS |
348 | } |
349 | ||
b8700d50 PDS |
350 | struct clk ** __init tegra_lookup_dt_id(int clk_id, |
351 | struct tegra_clk *tegra_clk) | |
352 | { | |
353 | if (tegra_clk[clk_id].present) | |
354 | return &clks[tegra_clk[clk_id].dt_id]; | |
355 | else | |
356 | return NULL; | |
357 | } | |
358 | ||
441f199a SW |
359 | tegra_clk_apply_init_table_func tegra_clk_apply_init_table; |
360 | ||
d0a57bd5 | 361 | static int __init tegra_clocks_apply_init_table(void) |
441f199a SW |
362 | { |
363 | if (!tegra_clk_apply_init_table) | |
d0a57bd5 | 364 | return 0; |
441f199a SW |
365 | |
366 | tegra_clk_apply_init_table(); | |
d0a57bd5 PDS |
367 | |
368 | return 0; | |
441f199a | 369 | } |
d0a57bd5 | 370 | arch_initcall(tegra_clocks_apply_init_table); |