Skip site navigation (1)Skip section navigation (2)
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>