aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-at91/Kconfig11
-rw-r--r--drivers/clk/at91/Makefile1
-rw-r--r--drivers/clk/at91/clk-usb.c398
-rw-r--r--drivers/clk/at91/pmc.c15
-rw-r--r--drivers/clk/at91/pmc.h9
5 files changed, 434 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 6ad37daca971..b76dc4c62af9 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -3,6 +3,9 @@ if ARCH_AT91
3config HAVE_AT91_UTMI 3config HAVE_AT91_UTMI
4 bool 4 bool
5 5
6config HAVE_AT91_USB_CLK
7 bool
8
6config HAVE_AT91_DBGU0 9config HAVE_AT91_DBGU0
7 bool 10 bool
8 11
@@ -82,6 +85,7 @@ config SOC_SAMA5D3
82 select HAVE_AT91_DBGU1 85 select HAVE_AT91_DBGU1
83 select AT91_USE_OLD_CLK 86 select AT91_USE_OLD_CLK
84 select HAVE_AT91_UTMI 87 select HAVE_AT91_UTMI
88 select HAVE_AT91_USB_CLK
85 help 89 help
86 Select this if you are using one of Atmel's SAMA5D3 family SoC. 90 Select this if you are using one of Atmel's SAMA5D3 family SoC.
87 This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35. 91 This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
@@ -96,12 +100,14 @@ config SOC_AT91RM9200
96 select MULTI_IRQ_HANDLER 100 select MULTI_IRQ_HANDLER
97 select SPARSE_IRQ 101 select SPARSE_IRQ
98 select AT91_USE_OLD_CLK 102 select AT91_USE_OLD_CLK
103 select HAVE_AT91_USB_CLK
99 104
100config SOC_AT91SAM9260 105config SOC_AT91SAM9260
101 bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20" 106 bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20"
102 select HAVE_AT91_DBGU0 107 select HAVE_AT91_DBGU0
103 select SOC_AT91SAM9 108 select SOC_AT91SAM9
104 select AT91_USE_OLD_CLK 109 select AT91_USE_OLD_CLK
110 select HAVE_AT91_USB_CLK
105 help 111 help
106 Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE 112 Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE
107 or AT91SAM9G20 SoC. 113 or AT91SAM9G20 SoC.
@@ -112,6 +118,7 @@ config SOC_AT91SAM9261
112 select HAVE_FB_ATMEL 118 select HAVE_FB_ATMEL
113 select SOC_AT91SAM9 119 select SOC_AT91SAM9
114 select AT91_USE_OLD_CLK 120 select AT91_USE_OLD_CLK
121 select HAVE_AT91_USB_CLK
115 help 122 help
116 Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC. 123 Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC.
117 124
@@ -121,6 +128,7 @@ config SOC_AT91SAM9263
121 select HAVE_FB_ATMEL 128 select HAVE_FB_ATMEL
122 select SOC_AT91SAM9 129 select SOC_AT91SAM9
123 select AT91_USE_OLD_CLK 130 select AT91_USE_OLD_CLK
131 select HAVE_AT91_USB_CLK
124 132
125config SOC_AT91SAM9RL 133config SOC_AT91SAM9RL
126 bool "AT91SAM9RL" 134 bool "AT91SAM9RL"
@@ -137,6 +145,7 @@ config SOC_AT91SAM9G45
137 select SOC_AT91SAM9 145 select SOC_AT91SAM9
138 select AT91_USE_OLD_CLK 146 select AT91_USE_OLD_CLK
139 select HAVE_AT91_UTMI 147 select HAVE_AT91_UTMI
148 select HAVE_AT91_USB_CLK
140 help 149 help
141 Select this if you are using one of Atmel's AT91SAM9G45 family SoC. 150 Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
142 This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11. 151 This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
@@ -148,6 +157,7 @@ config SOC_AT91SAM9X5
148 select SOC_AT91SAM9 157 select SOC_AT91SAM9
149 select AT91_USE_OLD_CLK 158 select AT91_USE_OLD_CLK
150 select HAVE_AT91_UTMI 159 select HAVE_AT91_UTMI
160 select HAVE_AT91_USB_CLK
151 help 161 help
152 Select this if you are using one of Atmel's AT91SAM9x5 family SoC. 162 Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
153 This means that your SAM9 name finishes with a '5' (except if it is 163 This means that your SAM9 name finishes with a '5' (except if it is
@@ -161,6 +171,7 @@ config SOC_AT91SAM9N12
161 select HAVE_FB_ATMEL 171 select HAVE_FB_ATMEL
162 select SOC_AT91SAM9 172 select SOC_AT91SAM9
163 select AT91_USE_OLD_CLK 173 select AT91_USE_OLD_CLK
174 select HAVE_AT91_USB_CLK
164 help 175 help
165 Select this if you are using Atmel's AT91SAM9N12 SoC. 176 Select this if you are using Atmel's AT91SAM9N12 SoC.
166 177
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index a82488323ea4..61db058be0c0 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -8,3 +8,4 @@ obj-y += clk-system.o clk-peripheral.o
8 8
9obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o 9obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS) += clk-programmable.o
10obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o 10obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
11obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
new file mode 100644
index 000000000000..7d1d26a4bd04
--- /dev/null
+++ b/drivers/clk/at91/clk-usb.c
@@ -0,0 +1,398 @@
1/*
2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 */
10
11#include <linux/clk-provider.h>
12#include <linux/clkdev.h>
13#include <linux/clk/at91_pmc.h>
14#include <linux/of.h>
15#include <linux/of_address.h>
16#include <linux/io.h>
17
18#include "pmc.h"
19
20#define USB_SOURCE_MAX 2
21
22#define SAM9X5_USB_DIV_SHIFT 8
23#define SAM9X5_USB_MAX_DIV 0xf
24
25#define RM9200_USB_DIV_SHIFT 28
26#define RM9200_USB_DIV_TAB_SIZE 4
27
28struct at91sam9x5_clk_usb {
29 struct clk_hw hw;
30 struct at91_pmc *pmc;
31};
32
33#define to_at91sam9x5_clk_usb(hw) \
34 container_of(hw, struct at91sam9x5_clk_usb, hw)
35
36struct at91rm9200_clk_usb {
37 struct clk_hw hw;
38 struct at91_pmc *pmc;
39 u32 divisors[4];
40};
41
42#define to_at91rm9200_clk_usb(hw) \
43 container_of(hw, struct at91rm9200_clk_usb, hw)
44
45static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
46 unsigned long parent_rate)
47{
48 u32 tmp;
49 u8 usbdiv;
50 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
51 struct at91_pmc *pmc = usb->pmc;
52
53 tmp = pmc_read(pmc, AT91_PMC_USB);
54 usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
55 return parent_rate / (usbdiv + 1);
56}
57
58static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
59 unsigned long *parent_rate)
60{
61 unsigned long div;
62 unsigned long bestrate;
63 unsigned long tmp;
64
65 if (rate >= *parent_rate)
66 return *parent_rate;
67
68 div = *parent_rate / rate;
69 if (div >= SAM9X5_USB_MAX_DIV)
70 return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
71
72 bestrate = *parent_rate / div;
73 tmp = *parent_rate / (div + 1);
74 if (bestrate - rate > rate - tmp)
75 bestrate = tmp;
76
77 return bestrate;
78}
79
80static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
81{
82 u32 tmp;
83 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
84 struct at91_pmc *pmc = usb->pmc;
85
86 if (index > 1)
87 return -EINVAL;
88 tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS;
89 if (index)
90 tmp |= AT91_PMC_USBS;
91 pmc_write(pmc, AT91_PMC_USB, tmp);
92 return 0;
93}
94
95static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
96{
97 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
98 struct at91_pmc *pmc = usb->pmc;
99
100 return pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS;
101}
102
103static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
104 unsigned long parent_rate)
105{
106 u32 tmp;
107 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
108 struct at91_pmc *pmc = usb->pmc;
109 unsigned long div = parent_rate / rate;
110
111 if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
112 return -EINVAL;
113
114 tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
115 tmp |= (div - 1) << SAM9X5_USB_DIV_SHIFT;
116 pmc_write(pmc, AT91_PMC_USB, tmp);
117
118 return 0;
119}
120
121static const struct clk_ops at91sam9x5_usb_ops = {
122 .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
123 .round_rate = at91sam9x5_clk_usb_round_rate,
124 .get_parent = at91sam9x5_clk_usb_get_parent,
125 .set_parent = at91sam9x5_clk_usb_set_parent,
126 .set_rate = at91sam9x5_clk_usb_set_rate,
127};
128
129static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
130{
131 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
132 struct at91_pmc *pmc = usb->pmc;
133
134 pmc_write(pmc, AT91_PMC_USB,
135 pmc_read(pmc, AT91_PMC_USB) | AT91_PMC_USBS);
136 return 0;
137}
138
139static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
140{
141 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
142 struct at91_pmc *pmc = usb->pmc;
143
144 pmc_write(pmc, AT91_PMC_USB,
145 pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS);
146}
147
148static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
149{
150 struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
151 struct at91_pmc *pmc = usb->pmc;
152
153 return !!(pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS);
154}
155
156static const struct clk_ops at91sam9n12_usb_ops = {
157 .enable = at91sam9n12_clk_usb_enable,
158 .disable = at91sam9n12_clk_usb_disable,
159 .is_enabled = at91sam9n12_clk_usb_is_enabled,
160 .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
161 .round_rate = at91sam9x5_clk_usb_round_rate,
162 .set_rate = at91sam9x5_clk_usb_set_rate,
163};
164
165static struct clk * __init
166at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name,
167 const char **parent_names, u8 num_parents)
168{
169 struct at91sam9x5_clk_usb *usb;
170 struct clk *clk = NULL;
171 struct clk_init_data init;
172
173 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
174 if (!usb)
175 return ERR_PTR(-ENOMEM);
176
177 init.name = name;
178 init.ops = &at91sam9x5_usb_ops;
179 init.parent_names = parent_names;
180 init.num_parents = num_parents;
181 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
182
183 usb->hw.init = &init;
184 usb->pmc = pmc;
185
186 clk = clk_register(NULL, &usb->hw);
187 if (IS_ERR(clk))
188 kfree(usb);
189
190 return clk;
191}
192
193static struct clk * __init
194at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name,
195 const char *parent_name)
196{
197 struct at91sam9x5_clk_usb *usb;
198 struct clk *clk = NULL;
199 struct clk_init_data init;
200
201 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
202 if (!usb)
203 return ERR_PTR(-ENOMEM);
204
205 init.name = name;
206 init.ops = &at91sam9n12_usb_ops;
207 init.parent_names = &parent_name;
208 init.num_parents = 1;
209 init.flags = CLK_SET_RATE_GATE;
210
211 usb->hw.init = &init;
212 usb->pmc = pmc;
213
214 clk = clk_register(NULL, &usb->hw);
215 if (IS_ERR(clk))
216 kfree(usb);
217
218 return clk;
219}
220
221static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
222 unsigned long parent_rate)
223{
224 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
225 struct at91_pmc *pmc = usb->pmc;
226 u32 tmp;
227 u8 usbdiv;
228
229 tmp = pmc_read(pmc, AT91_CKGR_PLLBR);
230 usbdiv = (tmp & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
231 if (usb->divisors[usbdiv])
232 return parent_rate / usb->divisors[usbdiv];
233
234 return 0;
235}
236
237static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
238 unsigned long *parent_rate)
239{
240 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
241 unsigned long bestrate = 0;
242 int bestdiff = -1;
243 unsigned long tmprate;
244 int tmpdiff;
245 int i = 0;
246
247 for (i = 0; i < 4; i++) {
248 if (!usb->divisors[i])
249 continue;
250 tmprate = *parent_rate / usb->divisors[i];
251 if (tmprate < rate)
252 tmpdiff = rate - tmprate;
253 else
254 tmpdiff = tmprate - rate;
255
256 if (bestdiff < 0 || bestdiff > tmpdiff) {
257 bestrate = tmprate;
258 bestdiff = tmpdiff;
259 }
260
261 if (!bestdiff)
262 break;
263 }
264
265 return bestrate;
266}
267
268static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
269 unsigned long parent_rate)
270{
271 u32 tmp;
272 int i;
273 struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
274 struct at91_pmc *pmc = usb->pmc;
275 unsigned long div = parent_rate / rate;
276
277 if (parent_rate % rate)
278 return -EINVAL;
279 for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
280 if (usb->divisors[i] == div) {
281 tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
282 ~AT91_PMC_USBDIV;
283 tmp |= i << RM9200_USB_DIV_SHIFT;
284 pmc_write(pmc, AT91_CKGR_PLLBR, tmp);
285 return 0;
286 }
287 }
288
289 return -EINVAL;
290}
291
292static const struct clk_ops at91rm9200_usb_ops = {
293 .recalc_rate = at91rm9200_clk_usb_recalc_rate,
294 .round_rate = at91rm9200_clk_usb_round_rate,
295 .set_rate = at91rm9200_clk_usb_set_rate,
296};
297
298static struct clk * __init
299at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
300 const char *parent_name, const u32 *divisors)
301{
302 struct at91rm9200_clk_usb *usb;
303 struct clk *clk = NULL;
304 struct clk_init_data init;
305
306 usb = kzalloc(sizeof(*usb), GFP_KERNEL);
307 if (!usb)
308 return ERR_PTR(-ENOMEM);
309
310 init.name = name;
311 init.ops = &at91rm9200_usb_ops;
312 init.parent_names = &parent_name;
313 init.num_parents = 1;
314 init.flags = 0;
315
316 usb->hw.init = &init;
317 usb->pmc = pmc;
318 memcpy(usb->divisors, divisors, sizeof(usb->divisors));
319
320 clk = clk_register(NULL, &usb->hw);
321 if (IS_ERR(clk))
322 kfree(usb);
323
324 return clk;
325}
326
327void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
328 struct at91_pmc *pmc)
329{
330 struct clk *clk;
331 int i;
332 int num_parents;
333 const char *parent_names[USB_SOURCE_MAX];
334 const char *name = np->name;
335
336 num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
337 if (num_parents <= 0 || num_parents > USB_SOURCE_MAX)
338 return;
339
340 for (i = 0; i < num_parents; i++) {
341 parent_names[i] = of_clk_get_parent_name(np, i);
342 if (!parent_names[i])
343 return;
344 }
345
346 of_property_read_string(np, "clock-output-names", &name);
347
348 clk = at91sam9x5_clk_register_usb(pmc, name, parent_names, num_parents);
349 if (IS_ERR(clk))
350 return;
351
352 of_clk_add_provider(np, of_clk_src_simple_get, clk);
353}
354
355void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
356 struct at91_pmc *pmc)
357{
358 struct clk *clk;
359 const char *parent_name;
360 const char *name = np->name;
361
362 parent_name = of_clk_get_parent_name(np, 0);
363 if (!parent_name)
364 return;
365
366 of_property_read_string(np, "clock-output-names", &name);
367
368 clk = at91sam9n12_clk_register_usb(pmc, name, parent_name);
369 if (IS_ERR(clk))
370 return;
371
372 of_clk_add_provider(np, of_clk_src_simple_get, clk);
373}
374
375void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
376 struct at91_pmc *pmc)
377{
378 struct clk *clk;
379 const char *parent_name;
380 const char *name = np->name;
381 u32 divisors[4] = {0, 0, 0, 0};
382
383 parent_name = of_clk_get_parent_name(np, 0);
384 if (!parent_name)
385 return;
386
387 of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
388 if (!divisors[0])
389 return;
390
391 of_property_read_string(np, "clock-output-names", &name);
392
393 clk = at91rm9200_clk_register_usb(pmc, name, parent_name, divisors);
394 if (IS_ERR(clk))
395 return;
396
397 of_clk_add_provider(np, of_clk_src_simple_get, clk);
398}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 0f667fd4e7fa..4fba500dec38 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -300,6 +300,21 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
300 .data = of_at91sam9x5_clk_utmi_setup, 300 .data = of_at91sam9x5_clk_utmi_setup,
301 }, 301 },
302#endif 302#endif
303 /* USB clock */
304#if defined(CONFIG_HAVE_AT91_USB_CLK)
305 {
306 .compatible = "atmel,at91rm9200-clk-usb",
307 .data = of_at91rm9200_clk_usb_setup,
308 },
309 {
310 .compatible = "atmel,at91sam9x5-clk-usb",
311 .data = of_at91sam9x5_clk_usb_setup,
312 },
313 {
314 .compatible = "atmel,at91sam9n12-clk-usb",
315 .data = of_at91sam9n12_clk_usb_setup,
316 },
317#endif
303 { /*sentinel*/ } 318 { /*sentinel*/ }
304}; 319};
305 320
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 8e18ab0d8e22..2483ddc0868b 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -99,4 +99,13 @@ extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
99 struct at91_pmc *pmc); 99 struct at91_pmc *pmc);
100#endif 100#endif
101 101
102#if defined(CONFIG_HAVE_AT91_USB_CLK)
103extern void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
104 struct at91_pmc *pmc);
105extern void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
106 struct at91_pmc *pmc);
107extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
108 struct at91_pmc *pmc);
109#endif
110
102#endif /* __PMC_H_ */ 111#endif /* __PMC_H_ */