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>