Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Mar 2009 15:50:53 GMT
From:      Ulf Lilleengen <lulf@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 159386 for review
Message-ID:  <200903181550.n2IForgS034483@repoman.freebsd.org>

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

Change 159386 by lulf@lulf_carrot on 2009/03/18 15:50:49

	- First try at implementing the devclk interface. A devclk structure
	  describing each clock is added, using KOBJ to make specifying clocks
	  quite easily. Clocks are specified where they belong logically, in our
	  case within the power manager. The power manager also registers these
	  clocks with the devclk manager at startup. There are several ways of
	  constructing the clock trees, but right now, there is an oscilliator
	  class which handles osc0, osc1, osc32, and a pll-class which handles
	  the plls. Just for testing, I added one class for the pbb clock being
	  used by MCI.
	
	         During probing, the hints will specify which clock a device will use,
	  so that when a device calls devclk_enable, the bus will lookup the
	  appropriate clock, and call devclk_activate on the clock to activate
	  it. Get/set rate should behave in the same way, and will be
	  implemented next. Further, a clock may have a parent, so that it may
	  call it's parents get_rate in order to determine it's rate. Also, a
	  clock may have an index into the parent clock as the parent may cover
	  several clocks. Exactly how this will be used is not clear yet.

Affected files ...

.. //depot/projects/avr32/src/sys/avr32/avr32/at32.c#7 edit
.. //depot/projects/avr32/src/sys/avr32/avr32/at32_pm.c#3 edit
.. //depot/projects/avr32/src/sys/avr32/conf/cpu/at32ap700x.hints#3 edit
.. //depot/projects/avr32/src/sys/kern/devclk_if.m#2 edit
.. //depot/projects/avr32/src/sys/kern/subr_devclk.c#2 edit
.. //depot/projects/avr32/src/sys/sys/devclk.h#2 edit

Differences ...

==== //depot/projects/avr32/src/sys/avr32/avr32/at32.c#7 (text+ko) ====

@@ -80,7 +80,7 @@
 };
 struct at32_ivar {
 	struct resource_list	resources;
-	int			clk_bus;
+	char			clk_name[32];
 	int			clk_index;
 };
 static device_method_t at32_methods[] = {
@@ -106,6 +106,7 @@
 	DEVMETHOD(devclk_disable,		at32_clk_disable),
 	{0, 0},
 };
+
 static driver_t at32_driver = {
 	"at32bus",
 	at32_methods,
@@ -128,6 +129,9 @@
 	int rid;
 	struct at32_softc *sc = device_get_softc(dev);
 
+	/* Initialize devclk manager. */
+	devclk_init();
+
 	/* Resource list for IRQ */
 	/* Reserve irqs from nexus ? */
 	sc->sc_irq_rman.rm_type = RMAN_ARRAY;
@@ -185,9 +189,13 @@
 static void
 at32_hinted_child(device_t bus, const char *dname, int dunit)
 {
+	/* XXX: Fetch ivar and set variables. */
 	device_t child;
 	long maddr;
 	int msize, irq, result;
+	const char *resval;
+	struct at32_ivar *ivar;
+	
 
 	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
 
@@ -210,6 +218,14 @@
 				"warning: bus_set_resource() failed\n");
 		}
 	}
+	ivar = device_get_ivars(child);
+	if (resource_string_value(dname, dunit, "clk", &resval) == 0) {
+		if (resource_int_value(dname, dunit, "clk_index",
+		    &ivar->clk_index) != 0)
+			ivar->clk_index = 0; /* Default */
+		strlcpy(ivar->clk_name, resval, sizeof(ivar->clk_name));
+	}
+
 }
 
 static struct resource *
@@ -361,11 +377,19 @@
 static void
 at32_clk_enable(device_t dev, device_t child)
 {
-	/* TODO: Implement */
+	struct at32_ivar *ivar = device_get_ivars(child);
+
+	/* Only activate if it actually has a clock. */
+	if (strcmp(ivar->clk_name, "") != 0)
+		devclk_activate(ivar->clk_name, ivar->clk_index);
 }
 
 static void
 at32_clk_disable(device_t dev, device_t child)
 {
-	/* TODO: Implement */
+	struct at32_ivar *ivar = device_get_ivars(child);
+
+	/* Only deactivate if it actually has a clock. */
+	if (strcmp(ivar->clk_name, "") != 0)
+		devclk_deactivate(ivar->clk_name, ivar->clk_index);
 }

==== //depot/projects/avr32/src/sys/avr32/avr32/at32_pm.c#3 (text+ko) ====

@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2009 Arnar Mar Sig
+ * Copyright (c) 2009 Ulf Lilleengen <lulf@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +38,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/kobj.h>
 #include <sys/module.h>
 #include <sys/time.h>
 #include <sys/bus.h>
@@ -63,6 +65,13 @@
 static int at32_pm_activate(device_t);
 static void at32_pm_deactivate(device_t);
 
+static void at32_pbb_start(devclk_t, uint8_t);
+static void at32_pbb_stop(devclk_t, uint8_t);
+static void at32_pll_start(devclk_t, uint8_t);
+static void at32_pll_stop(devclk_t, uint8_t);
+static void at32_osc_start(devclk_t, uint8_t);
+static void at32_osc_stop(devclk_t, uint8_t);
+
 /* Driver variables and private data */
 struct at32_pm_softc {
 	struct resource		*regs_res;
@@ -70,11 +79,13 @@
 	bus_space_tag_t		bst;
 	bus_space_handle_t	bsh;
 };
+
 static device_method_t at32_pm_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		at32_pm_probe),
 	DEVMETHOD(device_attach,	at32_pm_attach),
 	DEVMETHOD(device_detach,	at32_pm_detach),
+
 	{0, 0},
 };
 static driver_t at32_pm_driver = {
@@ -83,8 +94,39 @@
 	sizeof(struct at32_pm_softc),
 };
 static devclass_t at32_pm_devclass;
+
 DRIVER_MODULE(at32_pm, at32bus, at32_pm_driver, at32_pm_devclass, 0, 0);
 
+/* Class defining our oscilliator. */
+static kobj_method_t at32_osc_methods[] = {
+	KOBJMETHOD(devclk_start,	at32_osc_start),
+	KOBJMETHOD(devclk_stop,		at32_osc_stop),
+/*	KOBJMETHOD(devclk_set_rate,	at32_osc_set_rate),
+	KOBJMETHOD(devclk_get_rate,	at32_osc_get_rate),*/
+	{0, 0},
+};
+DEFINE_CLASS(at32_osc, at32_osc_methods, sizeof(struct devclk));
+
+/* Class defining our PLLs. */
+static kobj_method_t at32_pll_methods[] = {
+	KOBJMETHOD(devclk_start,	at32_pll_start),
+	KOBJMETHOD(devclk_stop,		at32_pll_stop),
+/*	KOBJMETHOD(devclk_set_rate,	at32_pll_set_rate),
+	KOBJMETHOD(devclk_get_rate,	at32_pll_get_rate),*/
+	{0, 0},
+};
+DEFINE_CLASS(at32_pll, at32_pll_methods, sizeof(struct devclk));
+
+/* Class defining the PBB clock mask. */
+static kobj_method_t at32_pbb_methods[] = {
+	KOBJMETHOD(devclk_start,	at32_pbb_start),
+	KOBJMETHOD(devclk_stop,		at32_pbb_stop),
+/*	KOBJMETHOD(devclk_set_rate,	at32_pbb_set_rate),
+	KOBJMETHOD(devclk_get_rate,	at32_pbb_get_rate),*/
+	{0, 0},
+};
+DEFINE_CLASS(at32_pbb, at32_pbb_methods, sizeof(struct devclk));
+
 /* Code */
 static int
 at32_pm_probe(device_t dev)
@@ -125,12 +167,29 @@
 	/* Set private data and map register space */
 	sc->regs_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->regs_rid, 0,
 		~0, 0, RF_ACTIVE);
+/*	sc->clock_res = bus_alloc_resource(dev, SYS_RES_CLOCK, &sc->clock_res,
+		RF_ACTIVE);*/
 	if (!sc->regs_res) {
 		goto err;
 	}
 	sc->bsh = rman_get_bushandle(sc->regs_res);
 	sc->bst = rman_get_bustag(sc->regs_res);
 
+	/* Register main clocks. */
+	//devclk_register_clock(dev, "osc32", NULL);
+	devclk_register_clock(dev, &at32_osc_class, "osc0", NULL);
+	//devclk_register_clock(dev, &sc->osc1, "osc1", NULL);
+
+	/* Register prescalers. */
+	devclk_register_clock(dev, &at32_pll_class, "pll0", "osc0");
+	//devclk_register_clock(dev, &sc->pll1, "pll1", &sc->osc0);
+
+	/* Register master device clocks. */
+	devclk_register_clock(dev, &at32_pbb_class, "pbb", "pll0");
+//	devclk_register_clock(dev, &sc->cpu, "cpu", &sc->pll0);
+//	devclk_register_clock(dev, &sc->hsb, "hsb", &sc->cpu);
+//	devclk_register_clock(dev, &sc->pba, "pba", &sc->hsb);
+//	devclk_register_clock(dev, &sc->pbb, "pbb", &sc->hsb);
 	return (0);
 
 err:
@@ -151,3 +210,81 @@
 	/* Turn off device clock */
 	devclk_disable(dev);
 }
+
+static void
+at32_pbb_start(devclk_t clk, uint8_t index)
+{
+	struct at32_pm_softc *sc;
+	uint32_t reg;
+
+	KASSERT(clk != NULL, ("NULL clk"));
+	KASSERT(index < 31, ("index > register width"));
+	sc = device_get_softc(clk->dev);
+	reg = RD4(AT32_PM_PBBMASK);
+	WR4(AT32_PM_PBBMASK, reg | (1 << index));
+}
+
+static void
+at32_pbb_stop(devclk_t clk, uint8_t index)
+{
+	struct at32_pm_softc *sc;
+	uint32_t reg;
+
+	KASSERT(clk != NULL, ("NULL clk"));
+	KASSERT(index < 31, ("index > register width"));
+	sc = device_get_softc(clk->dev);
+	reg = RD4(AT32_PM_PBBMASK);
+	WR4(AT32_PM_PBBMASK, reg & ~(1 << index));
+}
+
+static void
+at32_osc_start(devclk_t clk, uint8_t index)
+{
+	/* In this case, index means which oscilliator. */
+	switch (index) {
+	case 0:	/* OSC0 */
+		break;
+	case 1:	/* OSC1 */
+		break;
+	case 2:	/* OSC32 */
+		break;
+	}
+} 
+
+static void
+at32_osc_stop(devclk_t clk, uint8_t index)
+{
+	/* In this case, index means which oscilliator. */
+	switch (index) {
+	case 0:	/* OSC0 */
+		break;
+	case 1:	/* OSC1 */
+		break;
+	case 2:	/* OSC32 */
+		break;
+	}
+}
+
+static void
+at32_pll_start(devclk_t clk, uint8_t index)
+{
+	/* Here, index means which pll. */
+	switch (index) {
+	case 0:	/* PLL0. */
+		break;
+	case 1:	/* PLL1. */
+		break;
+	}
+} 
+
+static void
+at32_pll_stop(devclk_t clk, uint8_t index)
+{
+	/* Here, index means which pll. */
+	switch (index) {
+	case 0:	/* PLL0. */
+		break;
+	case 1:	/* PLL1. */
+		break;
+	}
+}

==== //depot/projects/avr32/src/sys/avr32/conf/cpu/at32ap700x.hints#3 (text+ko) ====

@@ -160,6 +160,8 @@
 hint.atmel_mci.0.maddr="0xFFF02400"
 hint.atmel_mci.0.msize="0x400"
 hint.atmel_mci.0.irq="28"
+hint.atmel_mci.0.clk="pbb"
+hint.atmel_mci.0.clk_index="9"
 
 hint.at32_ac97c.0.at="at32bus0"
 hint.at32_ac97c.0.maddr="0xFFF02800"

==== //depot/projects/avr32/src/sys/kern/devclk_if.m#2 (text+ko) ====

@@ -54,3 +54,15 @@
 	device_t	_dev;
 	device_t	_child;
 };
+
+# Enable a devclk
+METHOD void start {
+	devclk_t	_clk;
+	uint8_t		_index;
+};
+
+# Disable a devclk
+METHOD void stop {
+	devclk_t	_clk;
+	uint8_t		_index;
+};

==== //depot/projects/avr32/src/sys/kern/subr_devclk.c#2 (text+ko) ====

@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2009 Arnar Mar Sig <antab@antab.is>
+ * Copyright (c) 2009 Ulf Lilleengen <lulf@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,12 +31,24 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/bus.h>
 #include <sys/devclk.h>
+#include <sys/queue.h>
 
 #include "devclk_if.h"
 
+static devclk_list_t devclks;
+
+static devclk_t devclk_find_clock(const char *);
+
+void
+devclk_init(void)
+{
+	STAILQ_INIT(&devclks);
+}
+
 uint64_t
 devclk_get_rate(device_t dev)
 {
@@ -73,3 +86,60 @@
 		DEVCLK_DISABLE(parent, dev);
 	}
 }
+
+/**
+ * Register clock name handled by device dev, with the parent clock parent
+ */
+void
+devclk_register_clock(device_t dev, kobj_class_t cls, const char *name,
+    const char *parent)
+{
+	devclk_t clk;
+	
+	clk = malloc(sizeof(*clk), M_DEVBUF, M_WAITOK | M_ZERO);
+	clk->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
+	clk->dev = dev;
+	strlcpy(clk->name, name, sizeof(clk->name));
+	clk->parent = ((parent == NULL) ? NULL : devclk_find_clock(parent));
+
+	/* Insert clock into list. */
+	STAILQ_INSERT_HEAD(&devclks, clk, link);
+}
+
+static devclk_t
+devclk_find_clock(const char *name)
+{
+	devclk_t clk;
+
+	KASSERT(name != NULL, ("null name"));
+	STAILQ_FOREACH(clk, &devclks, link) {
+		if (strcmp(clk->name, name) == 0)
+			return (clk);
+	}
+	return (NULL);
+}
+
+/* Start device clock name by activating index index. */
+void
+devclk_activate(const char *name, uint8_t index)
+{
+	devclk_t clk;
+
+	clk = devclk_find_clock(name);
+	if (clk == NULL)
+		return;
+	/* XXX: Enable parent too ? */
+	DEVCLK_START(clk->methods, index);
+}
+
+/* Stop device clock name by deactivating index index. */
+void
+devclk_deactivate(const char *name, uint8_t index)
+{
+	devclk_t clk;
+
+	clk = devclk_find_clock(name);
+	if (clk == NULL)
+		return;
+	DEVCLK_STOP(clk->methods, index);
+}

==== //depot/projects/avr32/src/sys/sys/devclk.h#2 (text+ko) ====

@@ -1,9 +1,23 @@
 #ifndef _SYS_DEVCLK_H_
 #define _SYS_DEVCLK_H_
 
+#ifdef _KERNEL
+
+#include  <sys/kobj.h>
+struct devclk {
+	kobj_t methods;
+	device_t dev;		/* Device responsible for clock. */
+	char name[32];		/* Clock name.			 */
+	struct devclk *parent;	/* Clock we originate from.	 */
+	int index;		/* Our index in our parent. 	 */
+	STAILQ_ENTRY(devclk) link;
+};
+typedef struct devclk* devclk_t;
+typedef STAILQ_HEAD(, devclk) devclk_list_t;
+
 #include "devclk_if.h"
 
-#ifdef _KERNEL
+void devclk_init(void);
 
 /**
  * Get device clock rate
@@ -25,7 +39,14 @@
  */
 void devclk_disable(device_t);
 
+void devclk_activate(const char *, uint8_t);
+void devclk_deactivate(const char *, uint8_t);
+
+/**
+ * Add a clock to the devclk manager.
+ */
+void	devclk_register_clock(device_t, kobj_class_t, const char *, const char *);
+
 
 #endif /* _KERNEL */
 #endif /* !_SYS_DEVCLK_H_ */
-



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