diff options
author | andrew <andrew@FreeBSD.org> | 2012-07-26 08:01:25 +0000 |
---|---|---|
committer | andrew <andrew@FreeBSD.org> | 2012-07-26 08:01:25 +0000 |
commit | ab76299bf91c637be92880977cbd2e6fcb198045 (patch) | |
tree | b533679dee6da783dd8c09c9da14e74960f03a11 /sys/arm/at91/at91_pmc.c | |
parent | 8ebd4a20e01aa98aaab356ac5af5fdd0544153c3 (diff) | |
download | FreeBSD-src-ab76299bf91c637be92880977cbd2e6fcb198045.zip FreeBSD-src-ab76299bf91c637be92880977cbd2e6fcb198045.tar.gz |
Add support for the Atmel AT91SAM9G45 CPU.
Reviewed by: imp
Diffstat (limited to 'sys/arm/at91/at91_pmc.c')
-rw-r--r-- | sys/arm/at91/at91_pmc.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/sys/arm/at91/at91_pmc.c b/sys/arm/at91/at91_pmc.c index 799a87b..8711d17 100644 --- a/sys/arm/at91/at91_pmc.c +++ b/sys/arm/at91/at91_pmc.c @@ -65,6 +65,7 @@ MALLOC_DEFINE(M_PMC, "at91_pmc_clocks", "AT91 PMC Clock descriptors"); #define AT91_PMC_BASE 0xffffc00 static void at91_pmc_set_pllb_mode(struct at91_pmc_clock *, int); +static void at91_pmc_set_upll_mode(struct at91_pmc_clock *, int); static void at91_pmc_set_sys_mode(struct at91_pmc_clock *, int); static void at91_pmc_set_periph_mode(struct at91_pmc_clock *, int); static void at91_pmc_clock_alias(const char *name, const char *alias); @@ -110,6 +111,18 @@ static struct at91_pmc_clock pllb = { .set_mode = &at91_pmc_set_pllb_mode, }; +/* Used by USB on at91sam9g45 */ +static struct at91_pmc_clock upll = { + .name = "upll", // UTMI PLL, used for USB functions on 9G45 + .parent = &main_ck, + .refcnt = 0, + .id = 0, + .primary = 1, + .pll = 1, + .pmc_mask = (1 << 6), + .set_mode = &at91_pmc_set_upll_mode, +}; + static struct at91_pmc_clock udpck = { .name = "udpck", .parent = &pllb, @@ -143,6 +156,7 @@ static struct at91_pmc_clock *clock_list[16+32] = { &main_ck, &plla, &pllb, + &upll, &udpck, &uhpck, &mck, @@ -199,6 +213,26 @@ at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on) } static void +at91_pmc_set_upll_mode(struct at91_pmc_clock *clk, int on) +{ + struct at91_pmc_softc *sc = pmc_softc; + uint32_t value; + + if (on) { + on = PMC_IER_LOCKU; + value = CKGR_UCKR_UPLLEN | CKGR_UCKR_BIASEN; + } else + value = 0; + + WR4(sc, CKGR_UCKR, RD4(sc, CKGR_UCKR) | value); + while ((RD4(sc, PMC_SR) & PMC_IER_LOCKU) != on) + continue; + + WR4(sc, PMC_USB, PMC_USB_USBDIV(9) | PMC_USB_USBS); + WR4(sc, PMC_SCER, PMC_SCER_UHP_SAM9); +} + +static void at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on) { struct at91_pmc_softc *sc = pmc_softc; @@ -466,6 +500,12 @@ at91_pmc_init_clock(void) uhpck.pmc_mask = PMC_SCER_UHP_SAM9; udpck.pmc_mask = PMC_SCER_UDP_SAM9; } + /* There is no pllb on AT91SAM9G45 */ + if (at91_cpu_is(AT91_T_SAM9G45)) { + uhpck.parent = &upll; + uhpck.pmc_mask = PMC_SCER_UHP_SAM9; + } + mckr = RD4(sc, PMC_MCKR); main_ck.hz = main_clock; @@ -506,8 +546,14 @@ at91_pmc_init_clock(void) mdiv = (mckr & PMC_MCKR_MDIV_MASK) >> 8; if (at91_is_sam9() || at91_is_sam9xe()) { + /* + * On AT91SAM9G45 when mdiv == 3 we need to divide + * MCK by 3 but not, for example, on 9g20. + */ + if (!at91_cpu_is(AT91_T_SAM9G45) || mdiv <= 2) + mdiv *= 2; if (mdiv > 0) - mck.hz /= mdiv * 2; + mck.hz /= mdiv; } else mck.hz /= (1 + mdiv); |