Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 24 Feb 2006 01:29:51 GMT
From:      Warner Losh <imp@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 92309 for review
Message-ID:  <200602240129.k1O1Tpu2043925@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=92309

Change 92309 by imp@imp_Speedy on 2006/02/24 01:29:04

	Export clock management functionality.
	Turn on ohci clocks in its attach routine, turn them off in the detach.

Affected files ...

.. //depot/projects/arm/src/sys/arm/at91/at91_pmc.c#10 edit
.. //depot/projects/arm/src/sys/arm/at91/at91_pmcvar.h#1 add
.. //depot/projects/arm/src/sys/arm/at91/ohci_atmelarm.c#9 edit

Differences ...

==== //depot/projects/arm/src/sys/arm/at91/at91_pmc.c#10 (text+ko) ====

@@ -44,6 +44,7 @@
 #include <arm/at91/at91rm92reg.h>
 
 #include <arm/at91/at91_pmcreg.h>
+#include <arm/at91/at91_pmcvar.h>
 
 static struct at91_pmc_softc {
 	bus_space_tag_t		sc_st;
@@ -54,20 +55,6 @@
 	uint32_t		pllb_init;
 } *pmc_softc;
 
-struct at91_pmc_clock 
-{
-	const char	*name;
-	uint32_t	hz;
-	struct at91_pmc_clock *parent;
-	uint32_t	pmc_mask;
-	void		(*set_mode)(struct at91_pmc_clock *, int);
-	uint32_t	refcnt;
-	unsigned	id:2;
-	unsigned	primary:1;
-	unsigned	pll:1;
-	unsigned	programmable:1;
-};
-
 static void at91_pmc_set_pllb_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);
@@ -101,7 +88,7 @@
 static struct at91_pmc_clock pllb = {
 	.name = "pllb",		// PLLB Clock, used for USB functions
 	.parent = &main_ck,
-	.refcnt = 1,
+	.refcnt = 0,
 	.id = 0,
 	.primary = 1,
 	.pll = 1,
@@ -170,18 +157,72 @@
 static void
 at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on)
 {
+	struct at91_pmc_softc *sc = pmc_softc;
+	uint32_t value;
+
+	if (on) {
+		on = PMC_IER_LOCKB;
+		value = sc->pllb_init;
+	} else {
+		value = 0;
+	}
+	WR4(sc, CKGR_PLLBR, value);
+	while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
+		continue;
 }
 
 static void
 at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on)
 {
+	struct at91_pmc_softc *sc = pmc_softc;
+
+	WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask);
 }
 
 static void
 at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on)
 {
+	struct at91_pmc_softc *sc = pmc_softc;
+
+	WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask);
+}
+
+struct at91_pmc_clock *
+at91_pmc_clock_ref(const char *name)
+{
+	int i;
+
+	/* XXX LOCKING? XXX */
+	for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++)
+		if (strcmp(name, clock_list[i]->name) == 0)
+			return (clock_list[i]);
+
+	return (NULL);
+}
+
+void
+at91_pmc_clock_deref(struct at91_pmc_clock *clk)
+{
+}
+
+void
+at91_pmc_clock_enable(struct at91_pmc_clock *clk)
+{
+	if (clk->parent)
+		at91_pmc_clock_enable(clk->parent);
+	if (clk->refcnt++ == 0 && clk->set_mode)
+		clk->set_mode(clk, 1);
 }
 
+void
+at91_pmc_clock_disable(struct at91_pmc_clock *clk)
+{
+	if (--clk->refcnt == 0 && clk->set_mode)
+		clk->set_mode(clk, 0);
+	if (clk->parent)
+		at91_pmc_clock_disable(clk->parent);
+}
+
 static int
 at91_pmc_pll_rate(int freq, uint32_t reg, int is_pllb)
 {
@@ -197,7 +238,7 @@
 	}
 	if (is_pllb && (reg & (1 << 28)))
 		freq >>= 1;
-	return freq;
+	return (freq);
 }
 
 static uint32_t
@@ -269,7 +310,6 @@
 	 * this relationship.
 	 */
 	mckr = RD4(sc, PMC_MCKR);
-	printf("mckr is %x\n", mckr);
 	mck.parent = clock_list[mckr & 0x3];
 	mck.parent->refcnt++;
 	freq = mck.parent->hz;
@@ -283,8 +323,6 @@
 	    freq / 1000000, mck.hz / 1000000);
 	WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
 	    PMC_SCER_PCK3);
-	/* XXX -- enable all PMC clocks */
-	WR4(sc, PMC_PCER, 0xffffffff);
 	/* Disable all interrupts for PMC */
 	WR4(sc, PMC_IDR, 0xffffffff);
 }

==== //depot/projects/arm/src/sys/arm/at91/ohci_atmelarm.c#9 (text+ko) ====

@@ -43,11 +43,20 @@
 #include <dev/usb/ohcireg.h>
 #include <dev/usb/ohcivar.h>
 
+#include <arm/at91/at91_pmcvar.h>
+
 #define MEM_RID	0
 
 static int ohci_atmelarm_attach(device_t dev);
 static int ohci_atmelarm_detach(device_t dev);
 
+struct at91_ohci_softc
+{
+	struct ohci_softc sc_ohci;
+	struct at91_pmc_clock *iclk;
+	struct at91_pmc_clock *fclk;
+};
+
 static int
 ohci_atmelarm_probe(device_t dev)
 {
@@ -58,47 +67,57 @@
 static int
 ohci_atmelarm_attach(device_t dev)
 {
-	ohci_softc_t *sc = device_get_softc(dev);
+	struct at91_ohci_softc *sc = device_get_softc(dev);
 	int err;
 	int rid;
 
-	/* XXX need to enable clocks here, and some other stuff */
+	
+	sc->iclk = at91_pmc_clock_ref("ohci_clk");
+	sc->fclk = at91_pmc_clock_ref("uhpck");
 
 	rid = MEM_RID;
-	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	sc->sc_ohci.io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
 	    RF_ACTIVE);
-	if (sc->io_res == NULL) {
+	if (sc->sc_ohci.io_res == NULL) {
 		err = ENOMEM;
 		goto error;
 	}
-	sc->iot = rman_get_bustag(sc->io_res);
-	sc->ioh = rman_get_bushandle(sc->io_res);
+	sc->sc_ohci.iot = rman_get_bustag(sc->sc_ohci.io_res);
+	sc->sc_ohci.ioh = rman_get_bushandle(sc->sc_ohci.io_res);
 
 	rid = 0;
-	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	sc->sc_ohci.irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
 	    RF_ACTIVE);
-	if (sc->irq_res == NULL) {
+	if (sc->sc_ohci.irq_res == NULL) {
 		err = ENOMEM;
 		goto error;
 	}
-	sc->sc_bus.bdev = device_add_child(dev, "usb", -1);
-	if (sc->sc_bus.bdev == NULL) {
+	sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usb", -1);
+	if (sc->sc_ohci.sc_bus.bdev == NULL) {
 		err = ENOMEM;
 		goto error;
 	}
-	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+	device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus);
 
-	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO, ohci_intr, sc,
-	    &sc->ih);
+	err = bus_setup_intr(dev, sc->sc_ohci.irq_res, INTR_TYPE_BIO, ohci_intr, sc,
+	    &sc->sc_ohci.ih);
 	if (err) {
 		err = ENXIO;
 		goto error;
 	}
-	strlcpy(sc->sc_vendor, "Atmel", sizeof(sc->sc_vendor));
-	err = ohci_init(sc);
+	strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor));
+
+	/*
+	 * turn on the clocks from the AT91's point of view.  Keep the unit in reset.
+	 */
+	at91_pmc_clock_enable(sc->iclk);
+	at91_pmc_clock_enable(sc->fclk);
+	bus_space_write_4(sc->sc_ohci.iot, sc->sc_ohci.ioh, OHCI_CONTROL, 0);
+
+	err = ohci_init(&sc->sc_ohci);
 	if (!err) {
-		sc->sc_flags |= OHCI_SCFLG_DONEINIT;
-		err = device_probe_and_attach(sc->sc_bus.bdev);
+		sc->sc_ohci.sc_flags |= OHCI_SCFLG_DONEINIT;
+		err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev);
 	}
 
 error:;
@@ -112,30 +131,45 @@
 static int
 ohci_atmelarm_detach(device_t dev)
 {
-	ohci_softc_t *sc = device_get_softc(dev);
+	struct at91_ohci_softc *sc = device_get_softc(dev);
 
-	if (sc->sc_flags & OHCI_SCFLG_DONEINIT) {
-		ohci_detach(sc, 0);
-		sc->sc_flags &= ~OHCI_SCFLG_DONEINIT;
+	if (sc->sc_ohci.sc_flags & OHCI_SCFLG_DONEINIT) {
+		ohci_detach(&sc->sc_ohci, 0);
+		sc->sc_ohci.sc_flags &= ~OHCI_SCFLG_DONEINIT;
 	}
 
-	if (sc->ih) {
-		bus_teardown_intr(dev, sc->irq_res, sc->ih);
-		sc->ih = NULL;
+	/*
+	 * Put the controller into reset, then disable clocks and do
+	 * the MI tear down.  We have to disable the clocks/hardware
+	 * after we do the rest of the teardown.  We also disable the
+	 * clocks in the opposite order we acquire them, but that
+	 * doesn't seem to be absolutely necessary.  We free up the
+	 * clocks after we disable them, so the system could, in
+	 * theory, reuse them.
+	 */
+	bus_space_write_4(sc->sc_ohci.iot, sc->sc_ohci.ioh, OHCI_CONTROL, 0);
+	at91_pmc_clock_disable(sc->fclk);
+	at91_pmc_clock_disable(sc->iclk);
+	at91_pmc_clock_deref(sc->fclk);
+	at91_pmc_clock_deref(sc->iclk);
+
+	if (sc->sc_ohci.ih) {
+		bus_teardown_intr(dev, sc->sc_ohci.irq_res, sc->sc_ohci.ih);
+		sc->sc_ohci.ih = NULL;
 	}
-	if (sc->sc_bus.bdev) {
-		device_delete_child(dev, sc->sc_bus.bdev);
-		sc->sc_bus.bdev = NULL;
+	if (sc->sc_ohci.sc_bus.bdev) {
+		device_delete_child(dev, sc->sc_ohci.sc_bus.bdev);
+		sc->sc_ohci.sc_bus.bdev = NULL;
 	}
-	if (sc->irq_res) {
-		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
-		sc->irq_res = NULL;
+	if (sc->sc_ohci.irq_res) {
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.irq_res);
+		sc->sc_ohci.irq_res = NULL;
 	}
-	if (sc->io_res) {
-		bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->io_res);
-		sc->io_res = NULL;
-		sc->iot = 0;
-		sc->ioh = 0;
+	if (sc->sc_ohci.io_res) {
+		bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_ohci.io_res);
+		sc->sc_ohci.io_res = NULL;
+		sc->sc_ohci.iot = 0;
+		sc->sc_ohci.ioh = 0;
 	}
 	return (0);
 }
@@ -156,7 +190,7 @@
 static driver_t ohci_driver = {
 	"ohci",
 	ohci_methods,
-	sizeof(ohci_softc_t),
+	sizeof(struct at91_ohci_softc),
 };
 
 static devclass_t ohci_devclass;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200602240129.k1O1Tpu2043925>