Date: Fri, 17 Feb 2006 20:30:04 GMT From: Warner Losh <imp@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 91944 for review Message-ID: <200602172030.k1HKU44P052418@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=91944 Change 91944 by imp@imp_Speedy on 2006/02/17 20:29:09 Move clock hacking Affected files ... .. //depot/projects/arm/src/sys/arm/at91/at91_pmc.c#3 edit Differences ... ==== //depot/projects/arm/src/sys/arm/at91/at91_pmc.c#3 (text+ko) ==== @@ -49,14 +49,238 @@ bus_space_tag_t sc_st; bus_space_handle_t sc_sh; device_t dev; + int main_clock_hz; + uint32_t pllb_init; } *pmc_softc; +struct at91pmc_clock +{ + const char *name; + uint32_t hz; + struct at91pmc_clock *parent; + uint32_t pmc_mask; + void (*set_mode)(struct at91pmc_clock *, int); + uint32_t refcnt; + unsigned id:2; + unsigned primary:1; + unsigned pll:1; + unsigned programmable:1; +}; + +static void at91pmc_set_pllb_mode(struct at91pmc_clock *, int); +static void at91pmc_set_sys_mode(struct at91pmc_clock *, int); +static void at91pmc_set_periph_mode(struct at91pmc_clock *, int); + +static struct at91pmc_clock slck = { + .name = "slck", // 32,768 Hz slow clock + .hz = 32768, + .refcnt = 1, + .id = 0, + .primary = 1, +}; + +static struct at91pmc_clock main_ck = { + .name = "main", // Main clock + .refcnt = 1, + .id = 1, + .primary = 1, + .pmc_mask = PMC_IER_MOSCS, +}; + +static struct at91pmc_clock plla = { + .name = "plla", // PLLA Clock, used for CPU clocking + .parent = &main_ck, + .refcnt = 1, + .id = 0, + .primary = 1, + .pll = 1, + .pmc_mask = PMC_IER_LOCKA, +}; + +static struct at91pmc_clock pllb = { + .name = "pllb", // PLLB Clock, used for USB functions + .parent = &main_ck, + .refcnt = 1, + .id = 0, + .primary = 1, + .pll = 1, + .pmc_mask = PMC_IER_LOCKB, + .set_mode = &at91pmc_set_pllb_mode, +}; + +static struct at91pmc_clock udpck = { + .name = "udpck", + .parent = &pllb, + .pmc_mask = PMC_SCER_UDP, + .set_mode = at91pmc_set_sys_mode +}; + +static struct at91pmc_clock uhpck = { + .name = "uhpck", + .parent = &pllb, + .pmc_mask = PMC_SCER_UHP, + .set_mode = at91pmc_set_sys_mode +}; + +static struct at91pmc_clock mck = { + .name = "mck", + .pmc_mask = PMC_IER_MCKRDY, + .refcnt = 1, +}; + +static struct at91pmc_clock udc_clk = { + .name = "udc_clk", + .parent = &mck, + .pmc_mask = 1 << AT91RM92_IRQ_UDP, + .set_mode = &at91pmc_set_periph_mode +}; + +static struct at91pmc_clock ohci_clk = { + .name = "ohci_clk", + .parent = &mck, + .pmc_mask = 1 << AT91RM92_IRQ_UDP, + .set_mode = &at91pmc_set_periph_mode +}; + +static struct at91pmc_clock *const clock_list[] = { + &slck, + &main_ck, + &plla, + &pllb, + &udpck, + &uhpck, + &mck, + &udc_clk, + &ohci_clk +}; + #define RD4(off) \ bus_space_read_4(pmc_softc->sc_st, pmc_softc->sc_sh, (off)) #define WR4(off, val) \ bus_space_write_4(pmc_softc->sc_st, pmc_softc->sc_sh, (off), (val)) +static void +at91pmc_set_pllb_mode(struct at91pmc_clock *clk, int on) +{ +} + +static void +at91pmc_set_sys_mode(struct at91pmc_clock *clk, int on) +{ +} + +static void +at91pmc_set_periph_mode(struct at91pmc_clock *clk, int on) +{ +} + static int +at91pmc_pll_rate(int freq, uint32_t reg, int is_pllb) +{ + uint32_t mul, div; + + div = reg & 0xff; + mul = (reg >> 16) & 0x7ff; + if (div != 0 && mul != 0) { + freq /= div; + freq *= mul + 1; + } else { + freq = 0; + } + if (is_pllb && (reg & (1 << 28))) + freq >>= 1; + return freq; +} + +static uint32_t +at91pmc_pll_calc(uint32_t main_freq, uint32_t out_freq) +{ + uint32_t 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++) { + int32_t diff1; + uint32_t input, mul1; + + /* + * PLL input between 1MHz and 32MHz per spec, but lower + * frequences seem necessary in some cases so allow 100K. + */ + 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 void +at91pmc_init_clock(struct at91pmc_softc *sc, int main_clock) +{ + uint32_t mckr; + int freq; + + sc->main_clock_hz = main_clock; + + /* + * Initialize the usb clock. This sets up pllb, but disables the + * actual clock. + */ + sc->pllb_init = at91pmc_pll_calc(main_clock, 48000000 * 2) |0x10000000; + pllb.hz = at91pmc_pll_rate(main_clock, sc->pllb_init, 1); + WR4(PMC_PCDR, (1 << AT91RM92_IRQ_UHP) | (1 << AT91RM92_IRQ_UDP)); + WR4(PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP); + WR4(CKGR_PLLBR, 0); + WR4(PMC_SCER, PMC_SCER_MCKUDP); + + /* + * MCK and PCU derive from one of the primary clocks. Initialize + * this relationship. + */ + mckr = RD4(PMC_MCKR); + mck.parent = clock_list[mckr & 0x3]; + mck.parent->refcnt++; + freq = mck.parent->hz; + freq /= 1 << ((mckr >> 2) & 3); + mck.hz = freq / (1 + ((mckr >> 8) & 3)); + + printf("Main clock is %x\n", RD4(PMC_MCKR)); + + device_printf(sc->dev, "main clock = %d Hz PLLA = %d Hz CPU %d Hz main %d Hz\n", + sc->main_clock_hz, + at91pmc_pll_rate(main_clock, RD4(CKGR_PLLAR), 0), + freq, mck.hz); + WR4(PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 | + PMC_SCER_PCK3); +} + +static int at91pmc_probe(device_t dev) { @@ -75,17 +299,7 @@ if (bus_space_subregion(sc->sc_st, sc->sc_sh, AT91RM92_PMC_BASE, AT91RM92_PMC_SIZE, &pmc_softc->sc_sh) != 0) panic("couldn't subregion timer registers"); - printf("SCSR: %x\n", RD4(PMC_SCSR)); - WR4(PMC_PCER, 0xffffffff); - printf("PCSR: %x\n", RD4(PMC_PCSR)); - printf("MOR: %x\n", RD4(CKGR_MOR)); - printf("PLLA: %x\n", RD4(CKGR_PLLAR)); - printf("PLLB: %x\n", RD4(CKGR_PLLBR)); - printf("MCFR: %x\n", RD4(CKGR_MCFR)); - printf("MCFR: %x\n", RD4(CKGR_MCFR)); - printf("MCFR: %x\n", RD4(CKGR_MCFR)); - printf("MCKR: %x\n", RD4(PMC_MCKR)); - printf("SR: %x\n", RD4(PMC_SR)); + at91pmc_init_clock(pmc_softc, 10000000); return (0); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200602172030.k1HKU44P052418>