/* * File : clock.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006, RT-Thread Development Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://openlab.rt-thread.com/license/LICENSE * * Change Logs: * Date Author Notes * 2011-01-13 weety first version */ #include #include "rt_list.h" #include "at91sam926x.h" static rt_list_t clocks; struct clk { char name[32]; rt_uint32_t rate_hz; struct clk *parent; rt_list_t node; }; static struct clk clk32k = { .name = "clk32k", .rate_hz = AT91_SLOW_CLOCK, }; static struct clk main_clk = { .name = "main", }; static struct clk plla = { .name = "plla", }; static struct clk mck = { .name = "mck", }; static struct clk uhpck = { .name = "uhpck", }; static struct clk pllb = { .name = "pllb", .parent = &main_clk, }; static struct clk udpck = { .name = "udpck", .parent = &pllb, }; static struct clk *const standard_pmc_clocks[] = { /* four primary clocks */ &clk32k, &main_clk, &plla, /* MCK */ &mck }; /* clocks cannot be de-registered no refcounting necessary */ struct clk *clk_get(const char *id) { struct clk *clk; rt_list_t *list; for (list = (&clocks)->next; list != &clocks; list = list->next) { clk = (struct clk *)rt_list_entry(list, struct clk, node); if (strcmp(id, clk->name) == 0) return clk; } return RT_NULL; } rt_uint32_t clk_get_rate(struct clk *clk) { rt_uint32_t flags; rt_uint32_t rate; for (;;) { rate = clk->rate_hz; if (rate || !clk->parent) break; clk = clk->parent; } return rate; } static rt_uint32_t at91_pll_rate(struct clk *pll, rt_uint32_t freq, rt_uint32_t reg) { unsigned mul, div; div = reg & 0xff; mul = (reg >> 16) & 0x7ff; if (div && mul) { freq /= div; freq *= mul + 1; } else freq = 0; return freq; } static unsigned at91_pll_calc(unsigned main_freq, unsigned out_freq) { unsigned i, div = 0, mul = 0, diff = 1 << 30; unsigned ret = (out_freq > 155000000) ? 0xbe00 : 0x3e00; /* PLL output max 240 MHz (or 180 MHz per errata) */ if (out_freq > 240000000) goto fail; for (i = 1; i < 256; i++) { int diff1; unsigned input, mul1; /* * PLL input between 1MHz and 32MHz per spec, but lower * frequences seem necessary in some cases so allow 100K. * Warning: some newer products need 2MHz min. */ input = main_freq / i; if (input < 100000) continue; if (input > 32000000) continue; mul1 = out_freq / input; if (mul1 > 2048) continue; if (mul1 < 2) goto fail; diff1 = out_freq - input * mul1; if (diff1 < 0) diff1 = -diff1; if (diff > diff1) { diff = diff1; div = i; mul = mul1; if (diff == 0) break; } } if (i == 256 && diff > (out_freq >> 5)) goto fail; return ret | ((mul - 1) << 16) | div; fail: return 0; } static rt_uint32_t at91_usb_rate(struct clk *pll, rt_uint32_t freq, rt_uint32_t reg) { if (pll == &pllb && (reg & AT91_PMC_USB96M)) return freq / 2; else return freq; } /* PLLB generated USB full speed clock init */ static void at91_pllb_usbfs_clock_init(rt_uint32_t main_clock) { rt_uint32_t at91_pllb_usb_init; /* * USB clock init: choose 48 MHz PLLB value, * disable 48MHz clock during usb peripheral suspend. * * REVISIT: assumes MCK doesn't derive from PLLB! */ uhpck.parent = &pllb; at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | AT91_PMC_USB96M; pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init); at91_sys_write(AT91_CKGR_PLLBR, 0); udpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init); uhpck.rate_hz = at91_usb_rate(&pllb, pllb.rate_hz, at91_pllb_usb_init); } static struct clk *at91_css_to_clk(unsigned long css) { switch (css) { case AT91_PMC_CSS_SLOW: return &clk32k; case AT91_PMC_CSS_MAIN: return &main_clk; case AT91_PMC_CSS_PLLA: return &plla; case AT91_PMC_CSS_PLLB: return &pllb; } return RT_NULL; } #define false 0 #define true 1 int at91_clock_init(rt_uint32_t main_clock) { unsigned tmp, freq, mckr; int i; int pll_overclock = false; /* * When the bootloader initialized the main oscillator correctly, * there's no problem using the cycle counter. But if it didn't, * or when using oscillator bypass mode, we must be told the speed * of the main clock. */ if (!main_clock) { do { tmp = at91_sys_read(AT91_CKGR_MCFR); } while (!(tmp & AT91_PMC_MAINRDY)); main_clock = (tmp & AT91_PMC_MAINF) * (AT91_SLOW_CLOCK / 16); } main_clk.rate_hz = main_clock; /* report if PLLA is more than mildly overclocked */ plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_sys_read(AT91_CKGR_PLLAR)); if (plla.rate_hz > 209000000) pll_overclock = true; if (pll_overclock) ;//rt_kprintf("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000); at91_pllb_usbfs_clock_init(main_clock); /* * MCK and CPU derive from one of those primary clocks. * For now, assume this parentage won't change. */ mckr = at91_sys_read(AT91_PMC_MCKR); mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS); freq = mck.parent->rate_hz; freq /= (1 << ((mckr & AT91_PMC_PRES) >> 2)); /* prescale */ mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */ /* Register the PMC's standard clocks */ rt_list_init(&clocks); for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++) rt_list_insert_after(&clocks, &standard_pmc_clocks[i]->node); rt_list_insert_after(&clocks, &pllb.node); rt_list_insert_after(&clocks, &uhpck.node); rt_list_insert_after(&clocks, &udpck.node); /* MCK and CPU clock are "always on" */ //clk_enable(&mck); /*rt_kprintf("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n", freq / 1000000, (unsigned) mck.rate_hz / 1000000, (unsigned) main_clock / 1000000, ((unsigned) main_clock % 1000000) / 1000);*///cause blocked return 0; } /** * @brief System Clock Configuration */ void rt_hw_clock_init(void) { at91_clock_init(18432000); }