diff options
author | Andrew Victor <andrew@sanpeople.com> | 2006-06-19 08:20:23 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-06-19 08:20:23 -0400 |
commit | 91f8ed835ffb34b4108cc16eefd3303e4068bee0 (patch) | |
tree | cb600e909c298ef9bede94e99bd911611ddc86d6 | |
parent | b7408aff2d325581dcafffa5dbcc09c42ae64b5d (diff) |
[ARM] 3578/1: AT91RM9200 Clock update
Patch from Andrew Victor
Some updates to the clock infrastructure for the AT91RM9200.
1. Hard-coded values replaced with names defined in at91rm9200_sys.h.
2. Added the four PIO clocks, which are enabled at startup.
3. At startup, disable all unused clocks.
4. Minor bugfix for usage counts associated with MCK. [Patch from David
Brownell]
5. Added at91_clock_associate() function to associate device & function
with a particular clock. [Patch from David Brownell]
Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/mach-at91rm9200/clock.c | 121 |
1 files changed, 101 insertions, 20 deletions
diff --git a/arch/arm/mach-at91rm9200/clock.c b/arch/arm/mach-at91rm9200/clock.c index 8b95467c6d61..30042d2bac5f 100644 --- a/arch/arm/mach-at91rm9200/clock.c +++ b/arch/arm/mach-at91rm9200/clock.c | |||
@@ -32,8 +32,6 @@ | |||
32 | 32 | ||
33 | #include "generic.h" | 33 | #include "generic.h" |
34 | 34 | ||
35 | #undef DEBUG | ||
36 | |||
37 | /* | 35 | /* |
38 | * There's a lot more which can be done with clocks, including cpufreq | 36 | * There's a lot more which can be done with clocks, including cpufreq |
39 | * integration, slow clock mode support (for system suspend), letting | 37 | * integration, slow clock mode support (for system suspend), letting |
@@ -41,7 +39,9 @@ | |||
41 | */ | 39 | */ |
42 | 40 | ||
43 | struct clk { | 41 | struct clk { |
44 | const char *name; | 42 | const char *name; /* unique clock name */ |
43 | const char *function; /* function of the clock */ | ||
44 | struct device *dev; /* device associated with function */ | ||
45 | unsigned long rate_hz; | 45 | unsigned long rate_hz; |
46 | struct clk *parent; | 46 | struct clk *parent; |
47 | u32 pmc_mask; | 47 | u32 pmc_mask; |
@@ -71,15 +71,14 @@ static struct clk clk32k = { | |||
71 | }; | 71 | }; |
72 | static struct clk main_clk = { | 72 | static struct clk main_clk = { |
73 | .name = "main", | 73 | .name = "main", |
74 | .pmc_mask = 1 << 0, /* in PMC_SR */ | 74 | .pmc_mask = AT91_PMC_MOSCS, /* in PMC_SR */ |
75 | .users = 1, | ||
76 | .id = 1, | 75 | .id = 1, |
77 | .primary = 1, | 76 | .primary = 1, |
78 | }; | 77 | }; |
79 | static struct clk plla = { | 78 | static struct clk plla = { |
80 | .name = "plla", | 79 | .name = "plla", |
81 | .parent = &main_clk, | 80 | .parent = &main_clk, |
82 | .pmc_mask = 1 << 1, /* in PMC_SR */ | 81 | .pmc_mask = AT91_PMC_LOCKA, /* in PMC_SR */ |
83 | .id = 2, | 82 | .id = 2, |
84 | .primary = 1, | 83 | .primary = 1, |
85 | .pll = 1, | 84 | .pll = 1, |
@@ -105,7 +104,7 @@ static void pllb_mode(struct clk *clk, int is_on) | |||
105 | static struct clk pllb = { | 104 | static struct clk pllb = { |
106 | .name = "pllb", | 105 | .name = "pllb", |
107 | .parent = &main_clk, | 106 | .parent = &main_clk, |
108 | .pmc_mask = 1 << 2, /* in PMC_SR */ | 107 | .pmc_mask = AT91_PMC_LOCKB, /* in PMC_SR */ |
109 | .mode = pllb_mode, | 108 | .mode = pllb_mode, |
110 | .id = 3, | 109 | .id = 3, |
111 | .primary = 1, | 110 | .primary = 1, |
@@ -177,8 +176,7 @@ static struct clk pck3 = { | |||
177 | */ | 176 | */ |
178 | static struct clk mck = { | 177 | static struct clk mck = { |
179 | .name = "mck", | 178 | .name = "mck", |
180 | .pmc_mask = 1 << 3, /* in PMC_SR */ | 179 | .pmc_mask = AT91_PMC_MCKRDY, /* in PMC_SR */ |
181 | .users = 1, /* (must be) always on */ | ||
182 | }; | 180 | }; |
183 | 181 | ||
184 | static void pmc_periph_mode(struct clk *clk, int is_on) | 182 | static void pmc_periph_mode(struct clk *clk, int is_on) |
@@ -249,6 +247,30 @@ static struct clk spi_clk = { | |||
249 | .pmc_mask = 1 << AT91_ID_SPI, | 247 | .pmc_mask = 1 << AT91_ID_SPI, |
250 | .mode = pmc_periph_mode, | 248 | .mode = pmc_periph_mode, |
251 | }; | 249 | }; |
250 | static struct clk pioA_clk = { | ||
251 | .name = "pioA_clk", | ||
252 | .parent = &mck, | ||
253 | .pmc_mask = 1 << AT91_ID_PIOA, | ||
254 | .mode = pmc_periph_mode, | ||
255 | }; | ||
256 | static struct clk pioB_clk = { | ||
257 | .name = "pioB_clk", | ||
258 | .parent = &mck, | ||
259 | .pmc_mask = 1 << AT91_ID_PIOB, | ||
260 | .mode = pmc_periph_mode, | ||
261 | }; | ||
262 | static struct clk pioC_clk = { | ||
263 | .name = "pioC_clk", | ||
264 | .parent = &mck, | ||
265 | .pmc_mask = 1 << AT91_ID_PIOC, | ||
266 | .mode = pmc_periph_mode, | ||
267 | }; | ||
268 | static struct clk pioD_clk = { | ||
269 | .name = "pioD_clk", | ||
270 | .parent = &mck, | ||
271 | .pmc_mask = 1 << AT91_ID_PIOD, | ||
272 | .mode = pmc_periph_mode, | ||
273 | }; | ||
252 | 274 | ||
253 | static struct clk *const clock_list[] = { | 275 | static struct clk *const clock_list[] = { |
254 | /* four primary clocks -- MUST BE FIRST! */ | 276 | /* four primary clocks -- MUST BE FIRST! */ |
@@ -279,21 +301,46 @@ static struct clk *const clock_list[] = { | |||
279 | &udc_clk, | 301 | &udc_clk, |
280 | &twi_clk, | 302 | &twi_clk, |
281 | &spi_clk, | 303 | &spi_clk, |
304 | &pioA_clk, | ||
305 | &pioB_clk, | ||
306 | &pioC_clk, | ||
307 | &pioD_clk, | ||
282 | // ssc0..ssc2 | 308 | // ssc0..ssc2 |
283 | // tc0..tc5 | 309 | // tc0..tc5 |
310 | // irq0..irq6 | ||
284 | &ohci_clk, | 311 | &ohci_clk, |
285 | ðer_clk, | 312 | ðer_clk, |
286 | }; | 313 | }; |
287 | 314 | ||
288 | 315 | ||
316 | /* | ||
317 | * Associate a particular clock with a function (eg, "uart") and device. | ||
318 | * The drivers can then request the same 'function' with several different | ||
319 | * devices and not care about which clock name to use. | ||
320 | */ | ||
321 | void __init at91_clock_associate(const char *id, struct device *dev, const char *func) | ||
322 | { | ||
323 | struct clk *clk = clk_get(NULL, id); | ||
324 | |||
325 | if (!dev || !clk || !IS_ERR(clk_get(dev, func))) | ||
326 | return; | ||
327 | |||
328 | clk->function = func; | ||
329 | clk->dev = dev; | ||
330 | } | ||
331 | |||
289 | /* clocks are all static for now; no refcounting necessary */ | 332 | /* clocks are all static for now; no refcounting necessary */ |
290 | struct clk *clk_get(struct device *dev, const char *id) | 333 | struct clk *clk_get(struct device *dev, const char *id) |
291 | { | 334 | { |
292 | int i; | 335 | int i; |
293 | 336 | ||
294 | for (i = 0; i < ARRAY_SIZE(clock_list); i++) { | 337 | for (i = 0; i < ARRAY_SIZE(clock_list); i++) { |
295 | if (strcmp(id, clock_list[i]->name) == 0) | 338 | struct clk *clk = clock_list[i]; |
296 | return clock_list[i]; | 339 | |
340 | if (strcmp(id, clk->name) == 0) | ||
341 | return clk; | ||
342 | if (clk->function && (dev == clk->dev) && strcmp(id, clk->function) == 0) | ||
343 | return clk; | ||
297 | } | 344 | } |
298 | 345 | ||
299 | return ERR_PTR(-ENOENT); | 346 | return ERR_PTR(-ENOENT); |
@@ -593,6 +640,30 @@ fail: | |||
593 | return 0; | 640 | return 0; |
594 | } | 641 | } |
595 | 642 | ||
643 | |||
644 | /* | ||
645 | * Several unused clocks may be active. Turn them off. | ||
646 | */ | ||
647 | static void at91_periphclk_reset(void) | ||
648 | { | ||
649 | unsigned long reg; | ||
650 | int i; | ||
651 | |||
652 | reg = at91_sys_read(AT91_PMC_PCSR); | ||
653 | |||
654 | for (i = 0; i < ARRAY_SIZE(clock_list); i++) { | ||
655 | struct clk *clk = clock_list[i]; | ||
656 | |||
657 | if (clk->mode != pmc_periph_mode) | ||
658 | continue; | ||
659 | |||
660 | if (clk->users > 0) | ||
661 | reg &= ~clk->pmc_mask; | ||
662 | } | ||
663 | |||
664 | at91_sys_write(AT91_PMC_PCDR, reg); | ||
665 | } | ||
666 | |||
596 | int __init at91_clock_init(unsigned long main_clock) | 667 | int __init at91_clock_init(unsigned long main_clock) |
597 | { | 668 | { |
598 | unsigned tmp, freq, mckr; | 669 | unsigned tmp, freq, mckr; |
@@ -626,7 +697,6 @@ int __init at91_clock_init(unsigned long main_clock) | |||
626 | */ | 697 | */ |
627 | at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | AT91_PMC_USB96M; | 698 | at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | AT91_PMC_USB96M; |
628 | pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init); | 699 | pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init); |
629 | at91_sys_write(AT91_PMC_PCDR, (1 << AT91_ID_UHP) | (1 << AT91_ID_UDP)); | ||
630 | at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_UDP); | 700 | at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_UDP); |
631 | at91_sys_write(AT91_CKGR_PLLBR, 0); | 701 | at91_sys_write(AT91_CKGR_PLLBR, 0); |
632 | at91_sys_write(AT91_PMC_SCER, AT91_PMC_MCKUDP); | 702 | at91_sys_write(AT91_PMC_SCER, AT91_PMC_MCKUDP); |
@@ -640,11 +710,13 @@ int __init at91_clock_init(unsigned long main_clock) | |||
640 | */ | 710 | */ |
641 | mckr = at91_sys_read(AT91_PMC_MCKR); | 711 | mckr = at91_sys_read(AT91_PMC_MCKR); |
642 | mck.parent = clock_list[mckr & AT91_PMC_CSS]; | 712 | mck.parent = clock_list[mckr & AT91_PMC_CSS]; |
643 | mck.parent->users++; | ||
644 | freq = mck.parent->rate_hz; | 713 | freq = mck.parent->rate_hz; |
645 | freq /= (1 << ((mckr >> 2) & 3)); /* prescale */ | 714 | freq /= (1 << ((mckr >> 2) & 3)); /* prescale */ |
646 | mck.rate_hz = freq / (1 + ((mckr >> 8) & 3)); /* mdiv */ | 715 | mck.rate_hz = freq / (1 + ((mckr >> 8) & 3)); /* mdiv */ |
647 | 716 | ||
717 | /* MCK and CPU clock are "always on" */ | ||
718 | clk_enable(&mck); | ||
719 | |||
648 | printk("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n", | 720 | printk("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n", |
649 | freq / 1000000, (unsigned) mck.rate_hz / 1000000, | 721 | freq / 1000000, (unsigned) mck.rate_hz / 1000000, |
650 | (unsigned) main_clock / 1000000, | 722 | (unsigned) main_clock / 1000000, |
@@ -663,19 +735,28 @@ int __init at91_clock_init(unsigned long main_clock) | |||
663 | continue; | 735 | continue; |
664 | 736 | ||
665 | pckr = at91_sys_read(AT91_PMC_PCKR(clk->id)); | 737 | pckr = at91_sys_read(AT91_PMC_PCKR(clk->id)); |
666 | parent = clock_list[pckr & 3]; | 738 | parent = clock_list[pckr & AT91_PMC_CSS]; |
667 | clk->parent = parent; | 739 | clk->parent = parent; |
668 | clk->rate_hz = parent->rate_hz / (1 << ((pckr >> 2) & 3)); | 740 | clk->rate_hz = parent->rate_hz / (1 << ((pckr >> 2) & 3)); |
741 | |||
742 | if (clk->users == 0) { | ||
743 | /* not being used, so switch it off */ | ||
744 | at91_sys_write(AT91_PMC_SCDR, clk->pmc_mask); | ||
745 | } | ||
669 | } | 746 | } |
670 | #else | 747 | #else |
671 | /* disable unused clocks */ | 748 | /* disable all programmable clocks */ |
672 | at91_sys_write(AT91_PMC_SCDR, AT91_PMC_PCK0 | AT91_PMC_PCK1 | AT91_PMC_PCK2 | AT91_PMC_PCK3); | 749 | at91_sys_write(AT91_PMC_SCDR, AT91_PMC_PCK0 | AT91_PMC_PCK1 | AT91_PMC_PCK2 | AT91_PMC_PCK3); |
673 | #endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */ | 750 | #endif |
674 | 751 | ||
675 | /* FIXME several unused clocks may still be active... provide | 752 | /* enable the PIO clocks */ |
676 | * a CONFIG option to turn off all unused clocks at some point | 753 | clk_enable(&pioA_clk); |
677 | * before driver init starts. | 754 | clk_enable(&pioB_clk); |
678 | */ | 755 | clk_enable(&pioC_clk); |
756 | clk_enable(&pioD_clk); | ||
757 | |||
758 | /* disable all other unused peripheral clocks */ | ||
759 | at91_periphclk_reset(); | ||
679 | 760 | ||
680 | return 0; | 761 | return 0; |
681 | } | 762 | } |