PERFORCE change 91944 for review
Warner Losh
imp at FreeBSD.org
Fri Feb 17 12:30:16 PST 2006
http://perforce.freebsd.org/chv.cgi?CH=91944
Change 91944 by imp at 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);
}
More information about the p4-projects
mailing list