d9369891ab
frequency in the at91_pmc_clock_init rather than passing it in. Allow for frequencies >= 21MHz by rounding to the nearest 500Hz (Idea from Ian Lapore whose company uses a similar arrangement in their product). at91_pmc_clock_init() is now nearly independent of the rest of the pmc driver (which means we may be able to call it much earlier in boot soon to eliminate the master clock config file requirement for printf to work during early boot and also eliminate some interdependencies with the device ordering which requires pmc to be the first device added).
599 lines
14 KiB
C
599 lines
14 KiB
C
/*-
|
|
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
|
|
* Copyright (c) 2010 Greg Ansley. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/time.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/timetc.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/cpufunc.h>
|
|
#include <machine/resource.h>
|
|
#include <machine/frame.h>
|
|
#include <machine/intr.h>
|
|
#include <arm/at91/at91reg.h>
|
|
#include <arm/at91/at91var.h>
|
|
|
|
#include <arm/at91/at91_pmcreg.h>
|
|
#include <arm/at91/at91_pmcvar.h>
|
|
|
|
static struct at91_pmc_softc {
|
|
bus_space_tag_t sc_st;
|
|
bus_space_handle_t sc_sh;
|
|
struct resource *mem_res; /* Memory resource */
|
|
device_t dev;
|
|
uint32_t pllb_init;
|
|
} *pmc_softc;
|
|
|
|
MALLOC_DECLARE(M_PMC);
|
|
MALLOC_DEFINE(M_PMC, "at91_pmc_clocks", "AT91 PMC Clock descriptors");
|
|
|
|
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);
|
|
static void at91_pmc_clock_alias(const char *name, const char *alias);
|
|
|
|
static struct at91_pmc_clock slck = {
|
|
.name = "slck", // 32,768 Hz slow clock
|
|
.hz = 32768,
|
|
.refcnt = 1,
|
|
.id = 0,
|
|
.primary = 1,
|
|
};
|
|
|
|
/*
|
|
* NOTE: Clocks for "ordinary peripheral" devices e.g. spi0, udp0, uhp0 etc.
|
|
* are now created automatically. Only "system" clocks need be defined here.
|
|
*/
|
|
static struct at91_pmc_clock main_ck = {
|
|
.name = "main", // Main clock
|
|
.refcnt = 0,
|
|
.id = 1,
|
|
.primary = 1,
|
|
.pmc_mask = PMC_IER_MOSCS,
|
|
};
|
|
|
|
static struct at91_pmc_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 at91_pmc_clock pllb = {
|
|
.name = "pllb", // PLLB Clock, used for USB functions
|
|
.parent = &main_ck,
|
|
.refcnt = 0,
|
|
.id = 0,
|
|
.primary = 1,
|
|
.pll = 1,
|
|
.pmc_mask = PMC_IER_LOCKB,
|
|
.set_mode = &at91_pmc_set_pllb_mode,
|
|
};
|
|
|
|
static struct at91_pmc_clock udpck = {
|
|
.name = "udpck",
|
|
.parent = &pllb,
|
|
.pmc_mask = PMC_SCER_UDP,
|
|
.set_mode = at91_pmc_set_sys_mode
|
|
};
|
|
|
|
static struct at91_pmc_clock uhpck = {
|
|
.name = "uhpck",
|
|
.parent = &pllb,
|
|
.pmc_mask = PMC_SCER_UHP,
|
|
.set_mode = at91_pmc_set_sys_mode
|
|
};
|
|
|
|
static struct at91_pmc_clock mck = {
|
|
.name = "mck", // Master (Peripheral) Clock
|
|
.pmc_mask = PMC_IER_MCKRDY,
|
|
.refcnt = 0,
|
|
};
|
|
|
|
static struct at91_pmc_clock cpu = {
|
|
.name = "cpu", // CPU Clock
|
|
.parent = &plla,
|
|
.pmc_mask = PMC_SCER_PCK,
|
|
.refcnt = 0,
|
|
};
|
|
|
|
/* "+32" or the automatic peripheral clocks */
|
|
static struct at91_pmc_clock *clock_list[16+32] = {
|
|
&slck,
|
|
&main_ck,
|
|
&plla,
|
|
&pllb,
|
|
&udpck,
|
|
&uhpck,
|
|
&mck,
|
|
&cpu
|
|
};
|
|
|
|
static inline uint32_t
|
|
RD4(struct at91_pmc_softc *sc, bus_size_t off)
|
|
{
|
|
|
|
return (bus_read_4(sc->mem_res, off));
|
|
}
|
|
|
|
static inline void
|
|
WR4(struct at91_pmc_softc *sc, bus_size_t off, uint32_t val)
|
|
{
|
|
|
|
bus_write_4(sc->mem_res, off, val);
|
|
}
|
|
|
|
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;
|
|
|
|
/* Workaround RM9200 Errata #26 */
|
|
if (at91_is_rm92() &&
|
|
((value ^ RD4(sc, CKGR_PLLBR)) & 0x03f0ff) != 0) {
|
|
WR4(sc, CKGR_PLLBR, value ^ 1);
|
|
while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
if (on)
|
|
while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask)
|
|
continue;
|
|
else
|
|
while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask)
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
if (on)
|
|
while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask)
|
|
continue;
|
|
else
|
|
while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask)
|
|
continue;
|
|
}
|
|
|
|
struct at91_pmc_clock *
|
|
at91_pmc_clock_add(const char *name, uint32_t irq,
|
|
struct at91_pmc_clock *parent)
|
|
{
|
|
struct at91_pmc_clock *clk;
|
|
int i, buflen;
|
|
|
|
clk = malloc(sizeof(*clk), M_PMC, M_NOWAIT | M_ZERO);
|
|
if (clk == NULL)
|
|
goto err;
|
|
|
|
buflen = strlen(name) + 1;
|
|
clk->name = malloc(buflen, M_PMC, M_NOWAIT);
|
|
if (clk->name == NULL)
|
|
goto err;
|
|
|
|
strlcpy(clk->name, name, buflen);
|
|
clk->pmc_mask = 1 << irq;
|
|
clk->set_mode = &at91_pmc_set_periph_mode;
|
|
if (parent == NULL)
|
|
clk->parent = &mck;
|
|
else
|
|
clk->parent = parent;
|
|
|
|
for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
|
|
if (clock_list[i] == NULL) {
|
|
clock_list[i] = clk;
|
|
return (clk);
|
|
}
|
|
}
|
|
err:
|
|
if (clk != NULL) {
|
|
if (clk->name != NULL)
|
|
free(clk->name, M_PMC);
|
|
free(clk, M_PMC);
|
|
}
|
|
|
|
panic("could not allocate pmc clock '%s'", name);
|
|
return (NULL);
|
|
}
|
|
|
|
static void
|
|
at91_pmc_clock_alias(const char *name, const char *alias)
|
|
{
|
|
struct at91_pmc_clock *clk, *alias_clk;
|
|
|
|
clk = at91_pmc_clock_ref(name);
|
|
if (clk)
|
|
alias_clk = at91_pmc_clock_add(alias, 0, clk->parent);
|
|
|
|
if (clk && alias_clk) {
|
|
alias_clk->hz = clk->hz;
|
|
alias_clk->pmc_mask = clk->pmc_mask;
|
|
alias_clk->set_mode = clk->set_mode;
|
|
}
|
|
}
|
|
|
|
struct at91_pmc_clock *
|
|
at91_pmc_clock_ref(const char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
|
|
if (clock_list[i] == NULL)
|
|
break;
|
|
if (strcmp(name, clock_list[i]->name) == 0)
|
|
return (clock_list[i]);
|
|
}
|
|
|
|
//printf("at91_pmc: Warning - did not find clock '%s'", name);
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
at91_pmc_clock_deref(struct at91_pmc_clock *clk)
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
at91_pmc_clock_enable(struct at91_pmc_clock *clk)
|
|
{
|
|
|
|
/* XXX LOCKING? XXX */
|
|
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)
|
|
{
|
|
|
|
/* XXX LOCKING? XXX */
|
|
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(struct at91_pmc_clock *clk, uint32_t reg)
|
|
{
|
|
uint32_t mul, div, freq;
|
|
|
|
freq = clk->parent->hz;
|
|
div = (reg >> clk->pll_div_shift) & clk->pll_div_mask;
|
|
mul = (reg >> clk->pll_mul_shift) & clk->pll_mul_mask;
|
|
|
|
#if 0
|
|
printf("pll = (%d / %d) * %d = %d\n",
|
|
freq, div, mul + 1, (freq/div) * (mul+1));
|
|
#endif
|
|
|
|
if (div != 0 && mul != 0) {
|
|
freq /= div;
|
|
freq *= mul + 1;
|
|
} else
|
|
freq = 0;
|
|
clk->hz = freq;
|
|
|
|
return (freq);
|
|
}
|
|
|
|
static uint32_t
|
|
at91_pmc_pll_calc(struct at91_pmc_clock *clk, uint32_t out_freq)
|
|
{
|
|
uint32_t i, div = 0, mul = 0, diff = 1 << 30;
|
|
|
|
unsigned ret = 0x3e00;
|
|
|
|
if (out_freq > clk->pll_max_out)
|
|
goto fail;
|
|
|
|
for (i = 1; i < 256; i++) {
|
|
int32_t diff1;
|
|
uint32_t input, mul1;
|
|
|
|
input = clk->parent->hz / i;
|
|
if (input < clk->pll_min_in)
|
|
break;
|
|
if (input > clk->pll_max_in)
|
|
continue;
|
|
|
|
mul1 = out_freq / input;
|
|
if (mul1 > (clk->pll_mul_mask + 1))
|
|
continue;
|
|
if (mul1 == 0)
|
|
break;
|
|
|
|
diff1 = out_freq - input * mul1;
|
|
if (diff1 < 0)
|
|
diff1 = -diff1;
|
|
if (diff > diff1) {
|
|
diff = diff1;
|
|
div = i;
|
|
mul = mul1;
|
|
if (diff == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (diff > (out_freq >> PMC_PLL_SHIFT_TOL))
|
|
goto fail;
|
|
|
|
if (clk->set_outb != NULL)
|
|
ret |= clk->set_outb(out_freq);
|
|
|
|
return (ret |
|
|
((mul - 1) << clk->pll_mul_shift) |
|
|
(div << clk->pll_div_shift));
|
|
fail:
|
|
return (0);
|
|
}
|
|
|
|
#if !defined(AT91C_MAIN_CLOCK)
|
|
static const unsigned int at91_main_clock_tbl[] = {
|
|
3000000, 3276800, 3686400, 3840000, 4000000,
|
|
4433619, 4915200, 5000000, 5242880, 6000000,
|
|
6144000, 6400000, 6553600, 7159090, 7372800,
|
|
7864320, 8000000, 9830400, 10000000, 11059200,
|
|
12000000, 12288000, 13560000, 14318180, 14745600,
|
|
16000000, 17344700, 18432000, 20000000
|
|
};
|
|
#define MAIN_CLOCK_TBL_LEN (sizeof(at91_main_clock_tbl) / sizeof(*at91_main_clock_tbl))
|
|
|
|
static unsigned int
|
|
at91_pmc_sense_main_clock(struct at91_pmc_softc *sc)
|
|
{
|
|
unsigned int ckgr_val;
|
|
unsigned int diff, matchdiff, freq;
|
|
int i;
|
|
|
|
ckgr_val = (RD4(sc, CKGR_MCFR) & CKGR_MCFR_MAINF_MASK) << 11;
|
|
|
|
/*
|
|
* Clocks up to 50MHz can be connected to some models. If
|
|
* the frequency is >= 21MHz, assume that the slow clock can
|
|
* measure it correctly, and that any error can be adequately
|
|
* compensated for by roudning to the nearest 500Hz. Users
|
|
* with fast, or odd-ball clocks will need to set
|
|
* AT91C_MASTER_CLOCK in the kernel config file.
|
|
*/
|
|
if (ckgr_val >= 21000000)
|
|
return ((ckgr_val + 250) / 500 * 500);
|
|
|
|
/*
|
|
* Try to find the standard frequency that match best.
|
|
*/
|
|
freq = at91_main_clock_tbl[0];
|
|
matchdiff = abs(ckgr_val - at91_main_clock_tbl[0]);
|
|
for (i = 1; i < MAIN_CLOCK_TBL_LEN; i++) {
|
|
diff = abs(ckgr_val - at91_main_clock_tbl[i]);
|
|
if (diff < matchdiff) {
|
|
freq = at91_main_clock_tbl[i];
|
|
matchdiff = diff;
|
|
}
|
|
}
|
|
return (freq);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
at91_pmc_init_clock(struct at91_pmc_softc *sc)
|
|
{
|
|
unsigned int main_clock;
|
|
uint32_t mckr;
|
|
uint32_t mdiv;
|
|
|
|
#if !defined(AT91C_MAIN_CLOCK)
|
|
main_clock = at91_pmc_sense_main_clock(pmc_softc);
|
|
#else
|
|
main_clock = AT91C_MAIN_CLOCK;
|
|
#endif
|
|
|
|
if (at91_is_sam9() || at91_is_sam9xe()) {
|
|
uhpck.pmc_mask = PMC_SCER_UHP_SAM9;
|
|
udpck.pmc_mask = PMC_SCER_UDP_SAM9;
|
|
}
|
|
mckr = RD4(sc, PMC_MCKR);
|
|
main_ck.hz = main_clock;
|
|
|
|
at91_pmc_pll_rate(&plla, RD4(sc, CKGR_PLLAR));
|
|
|
|
if (at91_cpu_is(AT91_CPU_SAM9G45) && (mckr & PMC_MCKR_PLLADIV2))
|
|
plla.hz /= 2;
|
|
|
|
/*
|
|
* Initialize the usb clock. This sets up pllb, but disables the
|
|
* actual clock.
|
|
*/
|
|
sc->pllb_init = at91_pmc_pll_calc(&pllb, 48000000 * 2) | 0x10000000;
|
|
at91_pmc_pll_rate(&pllb, sc->pllb_init);
|
|
|
|
#if 0
|
|
/* Turn off USB clocks */
|
|
at91_pmc_set_periph_mode(&ohci_clk, 0);
|
|
at91_pmc_set_periph_mode(&udc_clk, 0);
|
|
#endif
|
|
|
|
if (at91_is_rm92()) {
|
|
WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP);
|
|
WR4(sc, PMC_SCER, PMC_SCER_MCKUDP);
|
|
} else
|
|
WR4(sc, PMC_SCDR, PMC_SCER_UHP_SAM9 | PMC_SCER_UDP_SAM9);
|
|
WR4(sc, CKGR_PLLBR, 0);
|
|
|
|
/*
|
|
* MCK and PCU derive from one of the primary clocks. Initialize
|
|
* this relationship.
|
|
*/
|
|
mck.parent = clock_list[mckr & 0x3];
|
|
mck.parent->refcnt++;
|
|
|
|
cpu.hz = mck.hz = mck.parent->hz /
|
|
(1 << ((mckr & PMC_MCKR_PRES_MASK) >> 2));
|
|
|
|
mdiv = (mckr & PMC_MCKR_MDIV_MASK) >> 8;
|
|
if (at91_is_sam9() || at91_is_sam9xe()) {
|
|
if (mdiv > 0)
|
|
mck.hz /= mdiv * 2;
|
|
} else
|
|
mck.hz /= (1 + mdiv);
|
|
|
|
/* Only found on SAM9G20 */
|
|
if (at91_cpu_is(AT91_CPU_SAM9G20))
|
|
cpu.hz /= (mckr & PMC_MCKR_PDIV) ? 2 : 1;
|
|
|
|
at91_master_clock = mck.hz;
|
|
|
|
/* These clocks refrenced by "special" names */
|
|
at91_pmc_clock_alias("ohci0", "ohci_clk");
|
|
at91_pmc_clock_alias("udp0", "udp_clk");
|
|
|
|
/* Turn off "Progamable" clocks */
|
|
WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
|
|
PMC_SCER_PCK3);
|
|
|
|
/* XXX kludge, turn on all peripherals */
|
|
WR4(sc, PMC_PCER, 0xffffffff);
|
|
|
|
/* Disable all interrupts for PMC */
|
|
WR4(sc, PMC_IDR, 0xffffffff);
|
|
}
|
|
|
|
static void
|
|
at91_pmc_deactivate(device_t dev)
|
|
{
|
|
struct at91_pmc_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
bus_generic_detach(sc->dev);
|
|
if (sc->mem_res)
|
|
bus_release_resource(dev, SYS_RES_IOPORT,
|
|
rman_get_rid(sc->mem_res), sc->mem_res);
|
|
sc->mem_res = 0;
|
|
}
|
|
|
|
static int
|
|
at91_pmc_activate(device_t dev)
|
|
{
|
|
struct at91_pmc_softc *sc;
|
|
int rid;
|
|
|
|
sc = device_get_softc(dev);
|
|
rid = 0;
|
|
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
RF_ACTIVE);
|
|
if (sc->mem_res == NULL)
|
|
goto errout;
|
|
return (0);
|
|
errout:
|
|
at91_pmc_deactivate(dev);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
static int
|
|
at91_pmc_probe(device_t dev)
|
|
{
|
|
|
|
device_set_desc(dev, "PMC");
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
at91_pmc_attach(device_t dev)
|
|
{
|
|
int err;
|
|
|
|
pmc_softc = device_get_softc(dev);
|
|
pmc_softc->dev = dev;
|
|
if ((err = at91_pmc_activate(dev)) != 0)
|
|
return (err);
|
|
|
|
/*
|
|
* Configure main clock frequency.
|
|
*/
|
|
at91_pmc_init_clock(pmc_softc);
|
|
|
|
device_printf(dev,
|
|
"Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n",
|
|
main_ck.hz,
|
|
plla.hz / 1000000,
|
|
cpu.hz / 1000000, mck.hz / 1000000);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static device_method_t at91_pmc_methods[] = {
|
|
DEVMETHOD(device_probe, at91_pmc_probe),
|
|
DEVMETHOD(device_attach, at91_pmc_attach),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t at91_pmc_driver = {
|
|
"at91_pmc",
|
|
at91_pmc_methods,
|
|
sizeof(struct at91_pmc_softc),
|
|
};
|
|
static devclass_t at91_pmc_devclass;
|
|
|
|
DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, NULL,
|
|
NULL);
|