Index: stable/10/sys/arm/arm/generic_timer.c
===================================================================
--- stable/10/sys/arm/arm/generic_timer.c (revision 270074)
+++ stable/10/sys/arm/arm/generic_timer.c (revision 270075)
@@ -1,390 +1,391 @@
/*-
* Copyright (c) 2011 The FreeBSD Foundation
* Copyright (c) 2013 Ruslan Bukin
* All rights reserved.
*
* Based on mpcore_timer.c developed by Ben Gray
*
* 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.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
*/
/**
* Cortex-A15 (and probably A7) Generic Timer
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GT_CTRL_ENABLE (1 << 0)
#define GT_CTRL_INT_MASK (1 << 1)
#define GT_CTRL_INT_STAT (1 << 2)
#define GT_REG_CTRL 0
#define GT_REG_TVAL 1
#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */
#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */
#define GT_CNTKCTL_EVNTI (1 << 4) /* Virtual counter event bits */
#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */
#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */
#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */
#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */
struct arm_tmr_softc {
struct resource *res[4];
void *ihl[4];
uint32_t clkfreq;
struct eventtimer et;
};
static struct arm_tmr_softc *arm_tmr_sc = NULL;
static struct resource_spec timer_spec[] = {
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* Secure */
{ SYS_RES_IRQ, 1, RF_ACTIVE }, /* Non-secure */
{ SYS_RES_IRQ, 2, RF_ACTIVE }, /* Virt */
{ SYS_RES_IRQ, 3, RF_ACTIVE }, /* Hyp */
{ -1, 0 }
};
static timecounter_get_t arm_tmr_get_timecount;
static struct timecounter arm_tmr_timecount = {
.tc_name = "ARM MPCore Timecounter",
.tc_get_timecount = arm_tmr_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_quality = 1000,
};
static inline int
get_freq(void)
{
uint32_t val;
__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
return (val);
}
static inline int
set_freq(uint32_t val)
{
__asm volatile("mcr p15, 0, %[val], c14, c0, 0" : :
[val] "r" (val));
isb();
return (val);
}
static inline long
get_cntpct(void)
{
uint64_t val;
__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
return (val);
}
static inline int
set_ctrl(uint32_t val)
{
__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
[val] "r" (val));
isb();
return (0);
}
static inline int
set_tval(uint32_t val)
{
__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
[val] "r" (val));
isb();
return (0);
}
static inline int
get_ctrl(void)
{
uint32_t val;
__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
return (val);
}
static inline int
get_tval(void)
{
uint32_t val;
__asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
return (val);
}
static inline void
disable_user_access(void)
{
uint32_t cntkctl;
__asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN);
__asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
isb();
}
static unsigned
arm_tmr_get_timecount(struct timecounter *tc)
{
return (get_cntpct());
}
static int
arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
{
struct arm_tmr_softc *sc;
int counts, ctrl;
sc = (struct arm_tmr_softc *)et->et_priv;
if (first != 0) {
counts = ((uint32_t)et->et_frequency * first) >> 32;
ctrl = get_ctrl();
ctrl &= ~GT_CTRL_INT_MASK;
ctrl |= GT_CTRL_ENABLE;
set_tval(counts);
set_ctrl(ctrl);
return (0);
}
return (EINVAL);
}
static int
arm_tmr_stop(struct eventtimer *et)
{
int ctrl;
ctrl = get_ctrl();
ctrl &= GT_CTRL_ENABLE;
set_ctrl(ctrl);
return (0);
}
static int
arm_tmr_intr(void *arg)
{
struct arm_tmr_softc *sc;
int ctrl;
sc = (struct arm_tmr_softc *)arg;
ctrl = get_ctrl();
if (ctrl & GT_CTRL_INT_STAT) {
ctrl |= GT_CTRL_INT_MASK;
set_ctrl(ctrl);
}
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
}
static int
arm_tmr_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "arm,armv7-timer"))
return (ENXIO);
device_set_desc(dev, "ARMv7 Generic Timer");
return (BUS_PROBE_DEFAULT);
}
static int
arm_tmr_attach(device_t dev)
{
struct arm_tmr_softc *sc;
phandle_t node;
pcell_t clock;
int error;
int i;
sc = device_get_softc(dev);
if (arm_tmr_sc)
return (ENXIO);
/* Get the base clock frequency */
node = ofw_bus_get_node(dev);
error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock));
if (error > 0) {
sc->clkfreq = fdt32_to_cpu(clock);
}
if (sc->clkfreq == 0) {
/* Try to get clock frequency from timer */
sc->clkfreq = get_freq();
}
if (sc->clkfreq == 0) {
device_printf(dev, "No clock frequency specified\n");
return (ENXIO);
}
if (bus_alloc_resources(dev, timer_spec, sc->res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
};
arm_tmr_sc = sc;
/* Setup secure and non-secure IRQs handler */
for (i = 0; i < 2; i++) {
error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK,
arm_tmr_intr, NULL, sc, &sc->ihl[i]);
if (error) {
device_printf(dev, "Unable to alloc int resource.\n");
return (ENXIO);
}
}
disable_user_access();
arm_tmr_timecount.tc_frequency = sc->clkfreq;
tc_init(&arm_tmr_timecount);
sc->et.et_name = "ARM MPCore Eventtimer";
sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
sc->et.et_quality = 1000;
sc->et.et_frequency = sc->clkfreq;
sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
sc->et.et_start = arm_tmr_start;
sc->et.et_stop = arm_tmr_stop;
sc->et.et_priv = sc;
et_register(&sc->et);
return (0);
}
static device_method_t arm_tmr_methods[] = {
DEVMETHOD(device_probe, arm_tmr_probe),
DEVMETHOD(device_attach, arm_tmr_attach),
{ 0, 0 }
};
static driver_t arm_tmr_driver = {
"generic_timer",
arm_tmr_methods,
sizeof(struct arm_tmr_softc),
};
static devclass_t arm_tmr_devclass;
-DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0);
+EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
void
DELAY(int usec)
{
int32_t counts, counts_per_usec;
uint32_t first, last;
/*
* Check the timers are setup, if not just
* use a for loop for the meantime
*/
if (arm_tmr_sc == NULL) {
for (; usec > 0; usec--)
for (counts = 200; counts > 0; counts--)
/*
* Prevent gcc from optimizing
* out the loop
*/
cpufunc_nullop();
return;
}
/* Get the number of times to count */
counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
/*
* Clamp the timeout at a maximum value (about 32 seconds with
* a 66MHz clock). *Nobody* should be delay()ing for anywhere
* near that length of time and if they are, they should be hung
* out to dry.
*/
if (usec >= (0x80000000U / counts_per_usec))
counts = (0x80000000U / counts_per_usec) - 1;
else
counts = usec * counts_per_usec;
first = get_cntpct();
while (counts > 0) {
last = get_cntpct();
counts -= (int32_t)(last - first);
first = last;
}
}
Index: stable/10/sys/arm/arm/gic.c
===================================================================
--- stable/10/sys/arm/arm/gic.c (revision 270074)
+++ stable/10/sys/arm/arm/gic.c (revision 270075)
@@ -1,407 +1,408 @@
/*-
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
*
* Developed by Damjan Marion
*
* Based on OMAP4 GIC code by Ben Gray
*
* 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.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 THE 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* We are using GICv2 register naming */
/* Distributor Registers */
#define GICD_CTLR 0x000 /* v1 ICDDCR */
#define GICD_TYPER 0x004 /* v1 ICDICTR */
#define GICD_IIDR 0x008 /* v1 ICDIIDR */
#define GICD_IGROUPR(n) (0x0080 + ((n) * 4)) /* v1 ICDISER */
#define GICD_ISENABLER(n) (0x0100 + ((n) * 4)) /* v1 ICDISER */
#define GICD_ICENABLER(n) (0x0180 + ((n) * 4)) /* v1 ICDICER */
#define GICD_ISPENDR(n) (0x0200 + ((n) * 4)) /* v1 ICDISPR */
#define GICD_ICPENDR(n) (0x0280 + ((n) * 4)) /* v1 ICDICPR */
#define GICD_ICACTIVER(n) (0x0380 + ((n) * 4)) /* v1 ICDABR */
#define GICD_IPRIORITYR(n) (0x0400 + ((n) * 4)) /* v1 ICDIPR */
#define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */
#define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */
#define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */
/* CPU Registers */
#define GICC_CTLR 0x0000 /* v1 ICCICR */
#define GICC_PMR 0x0004 /* v1 ICCPMR */
#define GICC_BPR 0x0008 /* v1 ICCBPR */
#define GICC_IAR 0x000C /* v1 ICCIAR */
#define GICC_EOIR 0x0010 /* v1 ICCEOIR */
#define GICC_RPR 0x0014 /* v1 ICCRPR */
#define GICC_HPPIR 0x0018 /* v1 ICCHPIR */
#define GICC_ABPR 0x001C /* v1 ICCABPR */
#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/
#define GIC_LAST_IPI 15 /* Irqs 0-15 are IPIs. */
/* First bit is a polarity bit (0 - low, 1 - high) */
#define GICD_ICFGR_POL_LOW (0 << 0)
#define GICD_ICFGR_POL_HIGH (1 << 0)
#define GICD_ICFGR_POL_MASK 0x1
/* Second bit is a trigger bit (0 - level, 1 - edge) */
#define GICD_ICFGR_TRIG_LVL (0 << 1)
#define GICD_ICFGR_TRIG_EDGE (1 << 1)
#define GICD_ICFGR_TRIG_MASK 0x2
struct arm_gic_softc {
struct resource * gic_res[3];
bus_space_tag_t gic_c_bst;
bus_space_tag_t gic_d_bst;
bus_space_handle_t gic_c_bsh;
bus_space_handle_t gic_d_bsh;
uint8_t ver;
device_t dev;
struct mtx mutex;
uint32_t nirqs;
};
static struct resource_spec arm_gic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */
{ SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */
{ -1, 0 }
};
static struct arm_gic_softc *arm_gic_sc = NULL;
#define gic_c_read_4(reg) \
bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg)
#define gic_c_write_4(reg, val) \
bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val)
#define gic_d_read_4(reg) \
bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg)
#define gic_d_write_4(reg, val) \
bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val)
static int gic_config_irq(int irq, enum intr_trigger trig,
enum intr_polarity pol);
static void gic_post_filter(void *);
static int
arm_gic_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "arm,gic"))
return (ENXIO);
device_set_desc(dev, "ARM Generic Interrupt Controller");
return (BUS_PROBE_DEFAULT);
}
void
gic_init_secondary(void)
{
int i, nirqs;
/* Get the number of interrupts */
nirqs = gic_d_read_4(GICD_TYPER);
nirqs = 32 * ((nirqs & 0x1f) + 1);
for (i = 0; i < nirqs; i += 4)
gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
/* Set all the interrupts to be in Group 0 (secure) */
for (i = 0; i < nirqs; i += 32) {
gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
}
/* Enable CPU interface */
gic_c_write_4(GICC_CTLR, 1);
/* Set priority mask register. */
gic_c_write_4(GICC_PMR, 0xff);
/* Enable interrupt distribution */
gic_d_write_4(GICD_CTLR, 0x01);
/* Activate IRQ 29, ie private timer IRQ*/
gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
}
static int
arm_gic_attach(device_t dev)
{
struct arm_gic_softc *sc;
int i;
uint32_t icciidr;
if (arm_gic_sc)
return (ENXIO);
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
/* Initialize mutex */
mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN);
/* Distributor Interface */
sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]);
sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]);
/* CPU Interface */
sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]);
sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]);
arm_gic_sc = sc;
/* Disable interrupt forwarding to the CPU interface */
gic_d_write_4(GICD_CTLR, 0x00);
/* Get the number of interrupts */
sc->nirqs = gic_d_read_4(GICD_TYPER);
sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1);
/* Set up function pointers */
arm_post_filter = gic_post_filter;
arm_config_irq = gic_config_irq;
icciidr = gic_c_read_4(GICC_IIDR);
device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x sc->nirqs %u\n",
icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf,
(icciidr & 0xfff), sc->nirqs);
/* Set all global interrupts to be level triggered, active low. */
for (i = 32; i < sc->nirqs; i += 16) {
gic_d_write_4(GICD_ICFGR(i >> 4), 0x00000000);
}
/* Disable all interrupts. */
for (i = 32; i < sc->nirqs; i += 32) {
gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF);
}
for (i = 0; i < sc->nirqs; i += 4) {
gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
gic_d_write_4(GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24);
}
/* Set all the interrupts to be in Group 0 (secure) */
for (i = 0; i < sc->nirqs; i += 32) {
gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
}
/* Enable CPU interface */
gic_c_write_4(GICC_CTLR, 1);
/* Set priority mask register. */
gic_c_write_4(GICC_PMR, 0xff);
/* Enable interrupt distribution */
gic_d_write_4(GICD_CTLR, 0x01);
return (0);
}
static device_method_t arm_gic_methods[] = {
DEVMETHOD(device_probe, arm_gic_probe),
DEVMETHOD(device_attach, arm_gic_attach),
{ 0, 0 }
};
static driver_t arm_gic_driver = {
"gic",
arm_gic_methods,
sizeof(struct arm_gic_softc),
};
static devclass_t arm_gic_devclass;
-DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0);
+EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
static void
gic_post_filter(void *arg)
{
uintptr_t irq = (uintptr_t) arg;
if (irq > GIC_LAST_IPI)
arm_irq_memory_barrier(irq);
gic_c_write_4(GICC_EOIR, irq);
}
int
arm_get_next_irq(int last_irq)
{
uint32_t active_irq;
active_irq = gic_c_read_4(GICC_IAR);
/*
* Immediatly EOIR the SGIs, because doing so requires the other
* bits (ie CPU number), not just the IRQ number, and we do not
* have this information later.
*/
if ((active_irq & 0x3ff) <= GIC_LAST_IPI)
gic_c_write_4(GICC_EOIR, active_irq);
active_irq &= 0x3FF;
if (active_irq == 0x3FF) {
if (last_irq == -1)
printf("Spurious interrupt detected\n");
return -1;
}
return active_irq;
}
void
arm_mask_irq(uintptr_t nb)
{
gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F)));
gic_c_write_4(GICC_EOIR, nb);
}
void
arm_unmask_irq(uintptr_t nb)
{
if (nb > GIC_LAST_IPI)
arm_irq_memory_barrier(nb);
gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F)));
}
static int
gic_config_irq(int irq, enum intr_trigger trig,
enum intr_polarity pol)
{
uint32_t reg;
uint32_t mask;
/* Function is public-accessible, so validate input arguments */
if ((irq < 0) || (irq >= arm_gic_sc->nirqs))
goto invalid_args;
if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) &&
(trig != INTR_TRIGGER_CONFORM))
goto invalid_args;
if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) &&
(pol != INTR_POLARITY_CONFORM))
goto invalid_args;
mtx_lock_spin(&arm_gic_sc->mutex);
reg = gic_d_read_4(GICD_ICFGR(irq >> 4));
mask = (reg >> 2*(irq % 16)) & 0x3;
if (pol == INTR_POLARITY_LOW) {
mask &= ~GICD_ICFGR_POL_MASK;
mask |= GICD_ICFGR_POL_LOW;
} else if (pol == INTR_POLARITY_HIGH) {
mask &= ~GICD_ICFGR_POL_MASK;
mask |= GICD_ICFGR_POL_HIGH;
}
if (trig == INTR_TRIGGER_LEVEL) {
mask &= ~GICD_ICFGR_TRIG_MASK;
mask |= GICD_ICFGR_TRIG_LVL;
} else if (trig == INTR_TRIGGER_EDGE) {
mask &= ~GICD_ICFGR_TRIG_MASK;
mask |= GICD_ICFGR_TRIG_EDGE;
}
/* Set mask */
reg = reg & ~(0x3 << 2*(irq % 16));
reg = reg | (mask << 2*(irq % 16));
gic_d_write_4(GICD_ICFGR(irq >> 4), reg);
mtx_unlock_spin(&arm_gic_sc->mutex);
return (0);
invalid_args:
device_printf(arm_gic_sc->dev, "gic_config_irg, invalid parameters\n");
return (EINVAL);
}
#ifdef SMP
void
pic_ipi_send(cpuset_t cpus, u_int ipi)
{
uint32_t val = 0, i;
for (i = 0; i < MAXCPU; i++)
if (CPU_ISSET(i, &cpus))
val |= 1 << (16 + i);
gic_d_write_4(GICD_SGIR(0), val | ipi);
}
int
pic_ipi_get(int i)
{
if (i != -1) {
/*
* The intr code will automagically give the frame pointer
* if the interrupt argument is 0.
*/
if ((unsigned int)i > 16)
return (0);
return (i);
}
return (0x3ff);
}
void
pic_ipi_clear(int ipi)
{
}
#endif
Index: stable/10/sys/arm/arm/mpcore_timer.c
===================================================================
--- stable/10/sys/arm/arm/mpcore_timer.c (revision 270074)
+++ stable/10/sys/arm/arm/mpcore_timer.c (revision 270075)
@@ -1,469 +1,470 @@
/*-
* Copyright (c) 2011 The FreeBSD Foundation
* All rights reserved.
*
* Developed by Ben Gray
*
* 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.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
*/
/**
* The ARM Cortex-A9 core can support a global timer plus a private and
* watchdog timer per core. This driver reserves memory and interrupt
* resources for accessing both timer register sets, these resources are
* stored globally and used to setup the timecount and eventtimer.
*
* The timecount timer uses the global 64-bit counter, whereas the
* per-CPU eventtimer uses the private 32-bit counters.
*
*
* REF: ARM Cortex-A9 MPCore, Technical Reference Manual (rev. r2p2)
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Private (per-CPU) timer register map */
#define PRV_TIMER_LOAD 0x0000
#define PRV_TIMER_COUNT 0x0004
#define PRV_TIMER_CTRL 0x0008
#define PRV_TIMER_INTR 0x000C
#define PRV_TIMER_CTR_PRESCALER_SHIFT 8
#define PRV_TIMER_CTRL_IRQ_ENABLE (1UL << 2)
#define PRV_TIMER_CTRL_AUTO_RELOAD (1UL << 1)
#define PRV_TIMER_CTRL_TIMER_ENABLE (1UL << 0)
#define PRV_TIMER_INTR_EVENT (1UL << 0)
/* Global timer register map */
#define GBL_TIMER_COUNT_LOW 0x0000
#define GBL_TIMER_COUNT_HIGH 0x0004
#define GBL_TIMER_CTRL 0x0008
#define GBL_TIMER_INTR 0x000C
#define GBL_TIMER_CTR_PRESCALER_SHIFT 8
#define GBL_TIMER_CTRL_AUTO_INC (1UL << 3)
#define GBL_TIMER_CTRL_IRQ_ENABLE (1UL << 2)
#define GBL_TIMER_CTRL_COMP_ENABLE (1UL << 1)
#define GBL_TIMER_CTRL_TIMER_ENABLE (1UL << 0)
#define GBL_TIMER_INTR_EVENT (1UL << 0)
struct arm_tmr_softc {
struct resource * tmr_res[4];
bus_space_tag_t prv_bst;
bus_space_tag_t gbl_bst;
bus_space_handle_t prv_bsh;
bus_space_handle_t gbl_bsh;
uint64_t clkfreq;
struct eventtimer et;
};
static struct resource_spec arm_tmr_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Global registers */
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* Global timer interrupt (unused) */
{ SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Private (per-CPU) registers */
{ SYS_RES_IRQ, 1, RF_ACTIVE }, /* Private timer interrupt */
{ -1, 0 }
};
static struct arm_tmr_softc *arm_tmr_sc = NULL;
static uint64_t platform_arm_tmr_freq = 0;
#define tmr_prv_read_4(reg) \
bus_space_read_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg)
#define tmr_prv_write_4(reg, val) \
bus_space_write_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg, val)
#define tmr_gbl_read_4(reg) \
bus_space_read_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg)
#define tmr_gbl_write_4(reg, val) \
bus_space_write_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg, val)
static timecounter_get_t arm_tmr_get_timecount;
static struct timecounter arm_tmr_timecount = {
.tc_name = "MPCore",
.tc_get_timecount = arm_tmr_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_quality = 800,
};
/**
* arm_tmr_get_timecount - reads the timecount (global) timer
* @tc: pointer to arm_tmr_timecount struct
*
* We only read the lower 32-bits, the timecount stuff only uses 32-bits
* so (for now?) ignore the upper 32-bits.
*
* RETURNS
* The lower 32-bits of the counter.
*/
static unsigned
arm_tmr_get_timecount(struct timecounter *tc)
{
return (tmr_gbl_read_4(GBL_TIMER_COUNT_LOW));
}
/**
* arm_tmr_start - starts the eventtimer (private) timer
* @et: pointer to eventtimer struct
* @first: the number of seconds and fractional sections to trigger in
* @period: the period (in seconds and fractional sections) to set
*
* If the eventtimer is required to be in oneshot mode, period will be
* NULL and first will point to the time to trigger. If in periodic mode
* period will contain the time period and first may optionally contain
* the time for the first period.
*
* RETURNS
* Always returns 0
*/
static int
arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
{
uint32_t load, count;
uint32_t ctrl;
tmr_prv_write_4(PRV_TIMER_CTRL, 0);
tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE;
if (period != 0) {
load = ((uint32_t)et->et_frequency * period) >> 32;
ctrl |= PRV_TIMER_CTRL_AUTO_RELOAD;
} else
load = 0;
if (first != 0)
count = (uint32_t)((et->et_frequency * first) >> 32);
else
count = load;
tmr_prv_write_4(PRV_TIMER_LOAD, load);
tmr_prv_write_4(PRV_TIMER_COUNT, count);
tmr_prv_write_4(PRV_TIMER_CTRL, ctrl);
return (0);
}
/**
* arm_tmr_stop - stops the eventtimer (private) timer
* @et: pointer to eventtimer struct
*
* Simply stops the private timer by clearing all bits in the ctrl register.
*
* RETURNS
* Always returns 0
*/
static int
arm_tmr_stop(struct eventtimer *et)
{
tmr_prv_write_4(PRV_TIMER_CTRL, 0);
tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
return (0);
}
/**
* arm_tmr_intr - ISR for the eventtimer (private) timer
* @arg: pointer to arm_tmr_softc struct
*
* Clears the event register and then calls the eventtimer callback.
*
* RETURNS
* Always returns FILTER_HANDLED
*/
static int
arm_tmr_intr(void *arg)
{
struct arm_tmr_softc *sc = (struct arm_tmr_softc *)arg;
tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
}
/**
* arm_tmr_probe - timer probe routine
* @dev: new device
*
* The probe function returns success when probed with the fdt compatible
* string set to "arm,mpcore-timers".
*
* RETURNS
* BUS_PROBE_DEFAULT if the fdt device is compatible, otherwise ENXIO.
*/
static int
arm_tmr_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "arm,mpcore-timers"))
return (ENXIO);
device_set_desc(dev, "ARM MPCore Timers");
return (BUS_PROBE_DEFAULT);
}
/**
* arm_tmr_attach - attaches the timer to the simplebus
* @dev: new device
*
* Reserves memory and interrupt resources, stores the softc structure
* globally and registers both the timecount and eventtimer objects.
*
* RETURNS
* Zero on sucess or ENXIO if an error occuried.
*/
static int
arm_tmr_attach(device_t dev)
{
struct arm_tmr_softc *sc = device_get_softc(dev);
phandle_t node;
pcell_t clock;
void *ihl;
boolean_t fixed_freq;
if (arm_tmr_sc)
return (ENXIO);
if (platform_arm_tmr_freq == ARM_TMR_FREQUENCY_VARIES) {
fixed_freq = false;
} else {
fixed_freq = true;
if (platform_arm_tmr_freq != 0) {
sc->clkfreq = platform_arm_tmr_freq;
} else {
/* Get the base clock frequency */
node = ofw_bus_get_node(dev);
if ((OF_getencprop(node, "clock-frequency", &clock,
sizeof(clock))) <= 0) {
device_printf(dev, "missing clock-frequency "
"attribute in FDT\n");
return (ENXIO);
}
sc->clkfreq = clock;
}
}
if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
/* Global timer interface */
sc->gbl_bst = rman_get_bustag(sc->tmr_res[0]);
sc->gbl_bsh = rman_get_bushandle(sc->tmr_res[0]);
/* Private per-CPU timer interface */
sc->prv_bst = rman_get_bustag(sc->tmr_res[2]);
sc->prv_bsh = rman_get_bushandle(sc->tmr_res[2]);
arm_tmr_sc = sc;
/* Disable both timers to start off */
tmr_prv_write_4(PRV_TIMER_CTRL, 0x00000000);
tmr_gbl_write_4(GBL_TIMER_CTRL, 0x00000000);
if (bus_setup_intr(dev, sc->tmr_res[3], INTR_TYPE_CLK, arm_tmr_intr,
NULL, sc, &ihl) != 0) {
bus_release_resources(dev, arm_tmr_spec, sc->tmr_res);
device_printf(dev, "Unable to setup the clock irq handler.\n");
return (ENXIO);
}
/*
* If the clock is fixed-frequency, setup and enable the global timer to
* use as the timecounter. If it's variable frequency it won't work as
* a timecounter. We also can't use it for DELAY(), so hopefully the
* platform provides its own implementation. If it doesn't, ours will
* get used, but since the frequency isn't set, it will only use the
* bogus loop counter.
*/
if (fixed_freq) {
tmr_gbl_write_4(GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE);
arm_tmr_timecount.tc_frequency = sc->clkfreq;
tc_init(&arm_tmr_timecount);
}
/*
* Setup and register the eventtimer. Most event timers set their min
* and max period values to some value calculated from the clock
* frequency. We might not know yet what our runtime clock frequency
* will be, so we just use some safe values. A max of 2 seconds ensures
* that even if our base clock frequency is 2GHz (meaning a 4GHz CPU),
* we won't overflow our 32-bit timer count register. A min of 20
* nanoseconds is pretty much completely arbitrary.
*/
sc->et.et_name = "MPCore";
sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
sc->et.et_quality = 1000;
sc->et.et_frequency = sc->clkfreq;
sc->et.et_min_period = 20 * SBT_1NS;
sc->et.et_max_period = 2 * SBT_1S;
sc->et.et_start = arm_tmr_start;
sc->et.et_stop = arm_tmr_stop;
sc->et.et_priv = sc;
et_register(&sc->et);
return (0);
}
static device_method_t arm_tmr_methods[] = {
DEVMETHOD(device_probe, arm_tmr_probe),
DEVMETHOD(device_attach, arm_tmr_attach),
{ 0, 0 }
};
static driver_t arm_tmr_driver = {
"mp_tmr",
arm_tmr_methods,
sizeof(struct arm_tmr_softc),
};
static devclass_t arm_tmr_devclass;
-DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0);
+EARLY_DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
+ BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
/*
* Handle a change in clock frequency. The mpcore timer runs at half the CPU
* frequency. When the CPU frequency changes due to power-saving or thermal
* managment, the platform-specific code that causes the frequency change calls
* this routine to inform the clock driver, and we in turn inform the event
* timer system, which actually updates the value in et->frequency for us and
* reschedules the current event(s) in a way that's atomic with respect to
* start/stop/intr code that may be running on various CPUs at the time of the
* call.
*
* This routine can also be called by a platform's early init code. If the
* value passed is ARM_TMR_FREQUENCY_VARIES, that will cause the attach() code
* to register as an eventtimer, but not a timecounter. If the value passed in
* is any other non-zero value it is used as the fixed frequency for the timer.
*/
void
arm_tmr_change_frequency(uint64_t newfreq)
{
if (arm_tmr_sc == NULL)
platform_arm_tmr_freq = newfreq;
else
et_change_frequency(&arm_tmr_sc->et, newfreq);
}
/**
* DELAY - Delay for at least usec microseconds.
* @usec: number of microseconds to delay by
*
* This function is called all over the kernel and is suppose to provide a
* consistent delay. This function may also be called before the console
* is setup so no printf's can be called here.
*
* RETURNS:
* nothing
*/
static void __used /* Must emit function code for the weak ref below. */
arm_tmr_DELAY(int usec)
{
int32_t counts_per_usec;
int32_t counts;
uint32_t first, last;
/* Check the timers are setup, if not just use a for loop for the meantime */
if (arm_tmr_sc == NULL || arm_tmr_timecount.tc_frequency == 0) {
for (; usec > 0; usec--)
for (counts = 200; counts > 0; counts--)
cpufunc_nullop(); /* Prevent gcc from optimizing
* out the loop
*/
return;
}
/* Get the number of times to count */
counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
/*
* Clamp the timeout at a maximum value (about 32 seconds with
* a 66MHz clock). *Nobody* should be delay()ing for anywhere
* near that length of time and if they are, they should be hung
* out to dry.
*/
if (usec >= (0x80000000U / counts_per_usec))
counts = (0x80000000U / counts_per_usec) - 1;
else
counts = usec * counts_per_usec;
first = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
while (counts > 0) {
last = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
counts -= (int32_t)(last - first);
first = last;
}
}
/*
* Supply a DELAY() implementation via weak linkage. A platform may want to use
* the mpcore per-cpu eventtimers but provide its own DELAY() routine,
* especially when the core frequency can change on the fly.
*/
__weak_reference(arm_tmr_DELAY, DELAY);
Index: stable/10/sys/arm/arm/nexus.c
===================================================================
--- stable/10/sys/arm/arm/nexus.c (revision 270074)
+++ stable/10/sys/arm/arm/nexus.c (revision 270075)
@@ -1,360 +1,365 @@
/*-
* Copyright 1998 Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that both the above copyright notice and this
* permission notice appear in all copies, that both the above
* copyright notice and this permission notice appear in all
* supporting documentation, and that the name of M.I.T. not be used
* in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission. M.I.T. makes
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
* SHALL M.I.T. 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.
*
*/
/*
* This code implements a `root nexus' for Arm Architecture
* machines. The function of the root nexus is to serve as an
* attachment point for both processors and buses, and to manage
* resources which are common to all of them. In particular,
* this code implements the core resource managers for interrupt
* requests, DMA requests (which rightfully should be a part of the
* ISA code but it's easier to do it here for now), I/O port addresses,
* and I/O memory address space.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "opt_platform.h"
#ifdef FDT
#include
#include
#include "ofw_bus_if.h"
#endif
static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
struct nexus_device {
struct resource_list nx_resources;
};
#define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
static struct rman mem_rman;
static int nexus_probe(device_t);
static int nexus_attach(device_t);
static int nexus_print_child(device_t, device_t);
static device_t nexus_add_child(device_t, u_int, const char *, int);
static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
static int nexus_activate_resource(device_t, device_t, int, int,
struct resource *);
static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol);
static int nexus_deactivate_resource(device_t, device_t, int, int,
struct resource *);
static int nexus_setup_intr(device_t dev, device_t child, struct resource *res,
int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep);
static int nexus_teardown_intr(device_t, device_t, struct resource *, void *);
#ifdef FDT
static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent,
int icells, pcell_t *intr);
#endif
static device_method_t nexus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, nexus_probe),
DEVMETHOD(device_attach, nexus_attach),
/* Bus interface */
DEVMETHOD(bus_print_child, nexus_print_child),
DEVMETHOD(bus_add_child, nexus_add_child),
DEVMETHOD(bus_alloc_resource, nexus_alloc_resource),
DEVMETHOD(bus_activate_resource, nexus_activate_resource),
DEVMETHOD(bus_config_intr, nexus_config_intr),
DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr),
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
#ifdef FDT
DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr),
#endif
{ 0, 0 }
};
static devclass_t nexus_devclass;
static driver_t nexus_driver = {
"nexus",
nexus_methods,
1 /* no softc */
};
+#ifdef ARM_DEVICE_MULTIPASS
+EARLY_DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
+#else
DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
+#endif
static int
nexus_probe(device_t dev)
{
device_quiet(dev); /* suppress attach message for neatness */
return (BUS_PROBE_DEFAULT);
}
static int
nexus_attach(device_t dev)
{
mem_rman.rm_start = 0;
mem_rman.rm_end = ~0ul;
mem_rman.rm_type = RMAN_ARRAY;
mem_rman.rm_descr = "I/O memory addresses";
if (rman_init(&mem_rman) || rman_manage_region(&mem_rman, 0, ~0))
panic("nexus_probe mem_rman");
/*
* First, deal with the children we know about already
*/
bus_generic_probe(dev);
bus_generic_attach(dev);
return (0);
}
static int
nexus_print_child(device_t bus, device_t child)
{
int retval = 0;
retval += bus_print_child_header(bus, child);
retval += printf("\n");
return (retval);
}
static device_t
nexus_add_child(device_t bus, u_int order, const char *name, int unit)
{
device_t child;
struct nexus_device *ndev;
ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
if (!ndev)
return (0);
resource_list_init(&ndev->nx_resources);
child = device_add_child_ordered(bus, order, name, unit);
/* should we free this in nexus_child_detached? */
device_set_ivars(child, ndev);
return (child);
}
/*
* Allocate a resource on behalf of child. NB: child is usually going to be a
* child of one of our descendants, not a direct child of nexus0.
* (Exceptions include footbridge.)
*/
static struct resource *
nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct resource *rv;
struct rman *rm;
int needactivate = flags & RF_ACTIVE;
switch (type) {
case SYS_RES_MEMORY:
case SYS_RES_IOPORT:
rm = &mem_rman;
break;
default:
return (0);
}
rv = rman_reserve_resource(rm, start, end, count, flags, child);
if (rv == 0)
return (0);
rman_set_rid(rv, *rid);
rman_set_bushandle(rv, rman_get_start(rv));
if (needactivate) {
if (bus_activate_resource(child, type, *rid, rv)) {
rman_release_resource(rv);
return (0);
}
}
return (rv);
}
static int
nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
{
int ret = ENODEV;
if (arm_config_irq)
ret = (*arm_config_irq)(irq, trig, pol);
return (ret);
}
static int
nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
{
int irq;
if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
flags |= INTR_EXCL;
for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
arm_setup_irqhandler(device_get_nameunit(child),
filt, intr, arg, irq, flags, cookiep);
arm_unmask_irq(irq);
}
return (0);
}
static int
nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
{
return (arm_remove_irqhandler(rman_get_start(r), ih));
}
static int
nexus_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
int err;
bus_addr_t paddr;
bus_size_t psize;
bus_space_handle_t vaddr;
if ((err = rman_activate_resource(r)) != 0)
return (err);
/*
* If this is a memory resource, map it into the kernel.
*/
if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
paddr = (bus_addr_t)rman_get_start(r);
psize = (bus_size_t)rman_get_size(r);
#ifdef FDT
err = bus_space_map(fdtbus_bs_tag, paddr, psize, 0, &vaddr);
if (err != 0) {
rman_deactivate_resource(r);
return (err);
}
rman_set_bustag(r, fdtbus_bs_tag);
#else
vaddr = (bus_space_handle_t)pmap_mapdev((vm_offset_t)paddr,
(vm_size_t)psize);
if (vaddr == 0) {
rman_deactivate_resource(r);
return (ENOMEM);
}
rman_set_bustag(r, (void *)1);
#endif
rman_set_virtual(r, (void *)vaddr);
rman_set_bushandle(r, vaddr);
}
return (0);
}
static int
nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
bus_size_t psize;
bus_space_handle_t vaddr;
psize = (bus_size_t)rman_get_size(r);
vaddr = rman_get_bushandle(r);
if (vaddr != 0) {
#ifdef FDT
bus_space_unmap(fdtbus_bs_tag, vaddr, psize);
#else
pmap_unmapdev((vm_offset_t)vaddr, (vm_size_t)psize);
#endif
rman_set_virtual(r, NULL);
rman_set_bushandle(r, 0);
}
return (rman_deactivate_resource(r));
}
#ifdef FDT
static int
nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
pcell_t *intr)
{
fdt_pic_decode_t intr_decode;
phandle_t intr_offset;
int i, rv, interrupt, trig, pol;
intr_offset = OF_xref_phandle(iparent);
for (i = 0; i < icells; i++)
intr[i] = cpu_to_fdt32(intr[i]);
for (i = 0; fdt_pic_table[i] != NULL; i++) {
intr_decode = fdt_pic_table[i];
rv = intr_decode(intr_offset, intr, &interrupt, &trig, &pol);
if (rv == 0) {
/* This was recognized as our PIC and decoded. */
interrupt = FDT_MAP_IRQ(intr_parent, interrupt);
return (interrupt);
}
}
/* Not in table, so guess */
interrupt = FDT_MAP_IRQ(intr_parent, fdt32_to_cpu(intr[0]));
return (interrupt);
}
#endif
Index: stable/10/sys/arm/arm/pl190.c
===================================================================
--- stable/10/sys/arm/arm/pl190.c (revision 270074)
+++ stable/10/sys/arm/arm/pl190.c (revision 270075)
@@ -1,191 +1,192 @@
/*-
* Copyright (c) 2012 Oleksandr Tymoshenko
* 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 THE 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 THE 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
#define dprintf(fmt, args...) printf(fmt, ##args)
#else
#define dprintf(fmt, args...)
#endif
#define VICIRQSTATUS 0x000
#define VICFIQSTATUS 0x004
#define VICRAWINTR 0x008
#define VICINTSELECT 0x00C
#define VICINTENABLE 0x010
#define VICINTENCLEAR 0x014
#define VICSOFTINT 0x018
#define VICSOFTINTCLEAR 0x01C
#define VICPROTECTION 0x020
#define VICPERIPHID 0xFE0
#define VICPRIMECELLID 0xFF0
#define VIC_NIRQS 32
struct pl190_intc_softc {
device_t sc_dev;
struct resource * intc_res;
};
static struct pl190_intc_softc *pl190_intc_sc = NULL;
#define intc_vic_read_4(reg) \
bus_read_4(pl190_intc_sc->intc_res, (reg))
#define intc_vic_write_4(reg, val) \
bus_write_4(pl190_intc_sc->intc_res, (reg), (val))
static int
pl190_intc_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
return (ENXIO);
device_set_desc(dev, "ARM PL190 VIC");
return (BUS_PROBE_DEFAULT);
}
static int
pl190_intc_attach(device_t dev)
{
struct pl190_intc_softc *sc = device_get_softc(dev);
uint32_t id;
int i, rid;
sc->sc_dev = dev;
if (pl190_intc_sc)
return (ENXIO);
/* Request memory resources */
rid = 0;
sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->intc_res == NULL) {
device_printf(dev, "Error: could not allocate memory resources\n");
return (ENXIO);
}
pl190_intc_sc = sc;
/*
* All interrupts should use IRQ line
*/
intc_vic_write_4(VICINTSELECT, 0x00000000);
/* Disable all interrupts */
intc_vic_write_4(VICINTENCLEAR, 0xffffffff);
/* Enable INT31, SIC IRQ */
intc_vic_write_4(VICINTENABLE, (1U << 31));
id = 0;
for (i = 3; i >= 0; i--) {
id = (id << 8) |
(intc_vic_read_4(VICPERIPHID + i*4) & 0xff);
}
device_printf(dev, "Peripheral ID: %08x\n", id);
id = 0;
for (i = 3; i >= 0; i--) {
id = (id << 8) |
(intc_vic_read_4(VICPRIMECELLID + i*4) & 0xff);
}
device_printf(dev, "PrimeCell ID: %08x\n", id);
return (0);
}
static device_method_t pl190_intc_methods[] = {
DEVMETHOD(device_probe, pl190_intc_probe),
DEVMETHOD(device_attach, pl190_intc_attach),
{ 0, 0 }
};
static driver_t pl190_intc_driver = {
"intc",
pl190_intc_methods,
sizeof(struct pl190_intc_softc),
};
static devclass_t pl190_intc_devclass;
-DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass, 0, 0);
+EARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
+ 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
int
arm_get_next_irq(int last_irq)
{
uint32_t pending;
int32_t irq = last_irq + 1;
/* Sanity check */
if (irq < 0)
irq = 0;
pending = intc_vic_read_4(VICIRQSTATUS);
while (irq < VIC_NIRQS) {
if (pending & (1 << irq))
return (irq);
irq++;
}
return (-1);
}
void
arm_mask_irq(uintptr_t nb)
{
dprintf("%s: %d\n", __func__, nb);
intc_vic_write_4(VICINTENCLEAR, (1 << nb));
}
void
arm_unmask_irq(uintptr_t nb)
{
dprintf("%s: %d\n", __func__, nb);
intc_vic_write_4(VICINTENABLE, (1 << nb));
}
Index: stable/10/sys/arm/arm/pl310.c
===================================================================
--- stable/10/sys/arm/arm/pl310.c (revision 270074)
+++ stable/10/sys/arm/arm/pl310.c (revision 270075)
@@ -1,517 +1,539 @@
/*-
* Copyright (c) 2012 Olivier Houchard
* Copyright (c) 2011
* Ben Gray .
* 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.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* Define this if you need to disable PL310 for debugging purpose
* Spec:
* http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
*/
/*
* Hardcode errata for now
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
*/
#define PL310_ERRATA_588369
#define PL310_ERRATA_753970
#define PL310_ERRATA_727915
#define PL310_LOCK(sc) do { \
mtx_lock_spin(&(sc)->sc_mtx); \
} while(0);
#define PL310_UNLOCK(sc) do { \
mtx_unlock_spin(&(sc)->sc_mtx); \
} while(0);
static int pl310_enabled = 1;
TUNABLE_INT("hw.pl310.enabled", &pl310_enabled);
static uint32_t g_l2cache_way_mask;
static const uint32_t g_l2cache_line_size = 32;
static const uint32_t g_l2cache_align_mask = (32 - 1);
static uint32_t g_l2cache_size;
static uint32_t g_way_size;
static uint32_t g_ways_assoc;
static struct pl310_softc *pl310_softc;
void
pl310_print_config(struct pl310_softc *sc)
{
uint32_t aux, prefetch;
const char *dis = "disabled";
const char *ena = "enabled";
aux = pl310_read4(sc, PL310_AUX_CTRL);
prefetch = pl310_read4(sc, PL310_PREFETCH_CTRL);
device_printf(sc->sc_dev, "Early BRESP response: %s\n",
(aux & AUX_CTRL_EARLY_BRESP) ? ena : dis);
device_printf(sc->sc_dev, "Instruction prefetch: %s\n",
(aux & AUX_CTRL_INSTR_PREFETCH) ? ena : dis);
device_printf(sc->sc_dev, "Data prefetch: %s\n",
(aux & AUX_CTRL_DATA_PREFETCH) ? ena : dis);
device_printf(sc->sc_dev, "Non-secure interrupt control: %s\n",
(aux & AUX_CTRL_NS_INT_CTRL) ? ena : dis);
device_printf(sc->sc_dev, "Non-secure lockdown: %s\n",
(aux & AUX_CTRL_NS_LOCKDOWN) ? ena : dis);
device_printf(sc->sc_dev, "Share override: %s\n",
(aux & AUX_CTRL_SHARE_OVERRIDE) ? ena : dis);
device_printf(sc->sc_dev, "Double linefill: %s\n",
(prefetch & PREFETCH_CTRL_DL) ? ena : dis);
device_printf(sc->sc_dev, "Instruction prefetch: %s\n",
(prefetch & PREFETCH_CTRL_INSTR_PREFETCH) ? ena : dis);
device_printf(sc->sc_dev, "Data prefetch: %s\n",
(prefetch & PREFETCH_CTRL_DATA_PREFETCH) ? ena : dis);
device_printf(sc->sc_dev, "Double linefill on WRAP request: %s\n",
(prefetch & PREFETCH_CTRL_DL_ON_WRAP) ? ena : dis);
device_printf(sc->sc_dev, "Prefetch drop: %s\n",
(prefetch & PREFETCH_CTRL_PREFETCH_DROP) ? ena : dis);
device_printf(sc->sc_dev, "Incr double Linefill: %s\n",
(prefetch & PREFETCH_CTRL_INCR_DL) ? ena : dis);
device_printf(sc->sc_dev, "Not same ID on exclusive sequence: %s\n",
(prefetch & PREFETCH_CTRL_NOTSAMEID) ? ena : dis);
device_printf(sc->sc_dev, "Prefetch offset: %d\n",
(prefetch & PREFETCH_CTRL_OFFSET_MASK));
}
void
pl310_set_ram_latency(struct pl310_softc *sc, uint32_t which_reg,
uint32_t read, uint32_t write, uint32_t setup)
{
uint32_t v;
KASSERT(which_reg == PL310_TAG_RAM_CTRL ||
which_reg == PL310_DATA_RAM_CTRL,
("bad pl310 ram latency register address"));
v = pl310_read4(sc, which_reg);
if (setup != 0) {
KASSERT(setup <= 8, ("bad pl310 setup latency: %d", setup));
v &= ~RAM_CTRL_SETUP_MASK;
v |= (setup - 1) << RAM_CTRL_SETUP_SHIFT;
}
if (read != 0) {
KASSERT(read <= 8, ("bad pl310 read latency: %d", read));
v &= ~RAM_CTRL_READ_MASK;
v |= (read - 1) << RAM_CTRL_READ_SHIFT;
}
if (write != 0) {
KASSERT(write <= 8, ("bad pl310 write latency: %d", write));
v &= ~RAM_CTRL_WRITE_MASK;
v |= (write - 1) << RAM_CTRL_WRITE_SHIFT;
}
pl310_write4(sc, which_reg, v);
}
static int
pl310_filter(void *arg)
{
struct pl310_softc *sc = arg;
uint32_t intr;
intr = pl310_read4(sc, PL310_INTR_MASK);
if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
/*
* This is for debug purpose, so be blunt about it
* We disable PL310 only when something fishy is going
* on and we need to make sure L2 cache is 100% disabled
*/
panic("pl310: caches disabled but cache event detected\n");
}
return (FILTER_HANDLED);
}
static __inline void
pl310_wait_background_op(uint32_t off, uint32_t mask)
{
while (pl310_read4(pl310_softc, off) & mask)
continue;
}
/**
* pl310_cache_sync - performs a cache sync operation
*
* According to the TRM:
*
* "Before writing to any other register you must perform an explicit
* Cache Sync operation. This is particularly important when the cache is
* enabled and changes to how the cache allocates new lines are to be made."
*
*
*/
static __inline void
pl310_cache_sync(void)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
#ifdef PL310_ERRATA_753970
if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
/* Write uncached PL310 register */
pl310_write4(pl310_softc, 0x740, 0xffffffff);
else
#endif
pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
}
static void
pl310_wbinv_all(void)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
PL310_LOCK(pl310_softc);
#ifdef PL310_ERRATA_727915
if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) {
int i, j;
for (i = 0; i < g_ways_assoc; i++) {
for (j = 0; j < g_way_size / g_l2cache_line_size; j++) {
pl310_write4(pl310_softc,
PL310_CLEAN_INV_LINE_IDX,
(i << 28 | j << 5));
}
}
pl310_cache_sync();
PL310_UNLOCK(pl310_softc);
return;
}
if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
platform_pl310_write_debug(pl310_softc, 3);
#endif
pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_cache_sync();
#ifdef PL310_ERRATA_727915
if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
platform_pl310_write_debug(pl310_softc, 0);
#endif
PL310_UNLOCK(pl310_softc);
}
static void
pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
#ifdef PL310_ERRATA_727915
platform_pl310_write_debug(pl310_softc, 3);
#endif
while (size > 0) {
#ifdef PL310_ERRATA_588369
if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) {
/*
* Errata 588369 says that clean + inv may keep the
* cache line if it was clean, the recommanded
* workaround is to clean then invalidate the cache
* line, with write-back and cache linefill disabled.
*/
pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
} else
#endif
pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA,
start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
#ifdef PL310_ERRATA_727915
platform_pl310_write_debug(pl310_softc, 0);
#endif
pl310_cache_sync();
PL310_UNLOCK(pl310_softc);
}
static void
pl310_wb_range(vm_paddr_t start, vm_size_t size)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
while (size > 0) {
pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
pl310_cache_sync();
PL310_UNLOCK(pl310_softc);
}
static void
pl310_inv_range(vm_paddr_t start, vm_size_t size)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
while (size > 0) {
pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
pl310_cache_sync();
PL310_UNLOCK(pl310_softc);
}
static void
pl310_drain_writebuf(void)
{
if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
return;
PL310_LOCK(pl310_softc);
pl310_cache_sync();
PL310_UNLOCK(pl310_softc);
}
static void
pl310_set_way_sizes(struct pl310_softc *sc)
{
uint32_t aux_value;
aux_value = pl310_read4(sc, PL310_AUX_CTRL);
g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
AUX_CTRL_WAY_SIZE_SHIFT;
g_way_size = 1 << (g_way_size + 13);
if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
g_ways_assoc = 16;
else
g_ways_assoc = 8;
g_l2cache_way_mask = (1 << g_ways_assoc) - 1;
g_l2cache_size = g_way_size * g_ways_assoc;
}
+/*
+ * Setup interrupt handling. This is done only if the cache controller is
+ * disabled, for debugging. We set counters so when a cache event happens we'll
+ * get interrupted and be warned that something is wrong, because no cache
+ * events should happen if we're disabled.
+ */
+static void
+pl310_config_intr(void *arg)
+{
+ struct pl310_softc * sc;
+
+ sc = arg;
+
+ /* activate the interrupt */
+ bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ pl310_filter, NULL, sc, &sc->sc_irq_h);
+
+ /* Cache Line Eviction for Counter 0 */
+ pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
+ /* Data Read Request for Counter 1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
+
+ /* Enable and clear pending interrupts */
+ pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
+ pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
+
+ /* Enable counters and reset C0 and C1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
+ EVENT_COUNTER_CTRL_ENABLED |
+ EVENT_COUNTER_CTRL_C0_RESET |
+ EVENT_COUNTER_CTRL_C1_RESET);
+
+ config_intrhook_disestablish(sc->sc_ich);
+ free(sc->sc_ich, M_DEVBUF);
+}
+
static int
pl310_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "arm,pl310"))
return (ENXIO);
device_set_desc(dev, "PL310 L2 cache controller");
return (0);
}
static int
pl310_attach(device_t dev)
{
struct pl310_softc *sc = device_get_softc(dev);
int rid;
uint32_t cache_id, debug_ctrl;
sc->sc_dev = dev;
rid = 0;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->sc_mem_res == NULL)
panic("%s: Cannot map registers", device_get_name(dev));
/* Allocate an IRQ resource */
rid = 0;
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE | RF_SHAREABLE);
if (sc->sc_irq_res == NULL) {
panic("Cannot allocate IRQ\n");
}
pl310_softc = sc;
mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
- /* activate the interrupt */
- bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
- pl310_filter, NULL, sc, &sc->sc_irq_h);
-
cache_id = pl310_read4(sc, PL310_CACHE_ID);
sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) &
CACHE_ID_RELEASE_MASK;
device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
(cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
(cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
/*
* If L2 cache is already enabled then something has violated the rules,
* because caches are supposed to be off at kernel entry. The cache
* must be disabled to write the configuration registers without
* triggering an access error (SLVERR), but there's no documented safe
* procedure for disabling the L2 cache in the manual. So we'll try to
* invent one:
* - Use the debug register to force write-through mode and prevent
* linefills (allocation of new lines on read); now anything we do
* will not cause new data to come into the L2 cache.
* - Writeback and invalidate the current contents.
* - Disable the controller.
* - Restore the original debug settings.
*/
if (pl310_read4(sc, PL310_CTRL) & CTRL_ENABLED) {
device_printf(dev, "Warning: L2 Cache should not already be "
"active; trying to de-activate and re-initialize...\n");
sc->sc_enabled = 1;
debug_ctrl = pl310_read4(sc, PL310_DEBUG_CTRL);
platform_pl310_write_debug(sc, debug_ctrl |
DEBUG_CTRL_DISABLE_WRITEBACK | DEBUG_CTRL_DISABLE_LINEFILL);
pl310_set_way_sizes(sc);
pl310_wbinv_all();
platform_pl310_write_ctrl(sc, CTRL_DISABLED);
platform_pl310_write_debug(sc, debug_ctrl);
}
sc->sc_enabled = pl310_enabled;
if (sc->sc_enabled) {
platform_pl310_init(sc);
pl310_set_way_sizes(sc); /* platform init might change these */
pl310_write4(pl310_softc, PL310_INV_WAY, 0xffff);
pl310_wait_background_op(PL310_INV_WAY, 0xffff);
platform_pl310_write_ctrl(sc, CTRL_ENABLED);
device_printf(dev, "L2 Cache enabled: %uKB/%dB %d ways\n",
(g_l2cache_size / 1024), g_l2cache_line_size, g_ways_assoc);
if (bootverbose)
pl310_print_config(sc);
} else {
- /*
- * Set counters so when cache event happens we'll get interrupt
- * and be warned that something is off.
- */
-
- /* Cache Line Eviction for Counter 0 */
- pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
- EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
- /* Data Read Request for Counter 1 */
- pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
- EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
-
- /* Enable and clear pending interrupts */
- pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
- pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
-
- /* Enable counters and reset C0 and C1 */
- pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
- EVENT_COUNTER_CTRL_ENABLED |
- EVENT_COUNTER_CTRL_C0_RESET |
- EVENT_COUNTER_CTRL_C1_RESET);
-
+ malloc(sizeof(*sc->sc_ich), M_DEVBUF, M_WAITOK);
+ sc->sc_ich->ich_func = pl310_config_intr;
+ sc->sc_ich->ich_arg = sc;
+ if (config_intrhook_establish(sc->sc_ich) != 0) {
+ device_printf(dev,
+ "config_intrhook_establish failed\n");
+ return(ENXIO);
+ }
device_printf(dev, "L2 Cache disabled\n");
}
/* Set the l2 functions in the set of cpufuncs */
cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
cpufuncs.cf_l2cache_drain_writebuf = pl310_drain_writebuf;
return (0);
}
static device_method_t pl310_methods[] = {
DEVMETHOD(device_probe, pl310_probe),
DEVMETHOD(device_attach, pl310_attach),
DEVMETHOD_END
};
static driver_t pl310_driver = {
"l2cache",
pl310_methods,
sizeof(struct pl310_softc),
};
static devclass_t pl310_devclass;
-DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
+EARLY_DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0,
+ BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE);
+
Index: stable/10/sys/arm/include/pl310.h
===================================================================
--- stable/10/sys/arm/include/pl310.h (revision 270074)
+++ stable/10/sys/arm/include/pl310.h (revision 270075)
@@ -1,185 +1,188 @@
/*-
* Copyright (c) 2012 Olivier Houchard. 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 THE AUTHOR ``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 THE AUTHOR 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.
*/
/*
* $FreeBSD$
*/
#ifndef PL310_H_
#define PL310_H_
/**
* PL310 - L2 Cache Controller register offsets.
*
*/
#define PL310_CACHE_ID 0x000
#define CACHE_ID_RELEASE_SHIFT 0
#define CACHE_ID_RELEASE_MASK 0x3f
#define CACHE_ID_RELEASE_r0p0 0x00
#define CACHE_ID_RELEASE_r1p0 0x02
#define CACHE_ID_RELEASE_r2p0 0x04
#define CACHE_ID_RELEASE_r3p0 0x05
#define CACHE_ID_RELEASE_r3p1 0x06
#define CACHE_ID_RELEASE_r3p2 0x08
#define CACHE_ID_RELEASE_r3p3 0x09
#define CACHE_ID_PARTNUM_SHIFT 6
#define CACHE_ID_PARTNUM_MASK 0xf
#define CACHE_ID_PARTNUM_VALUE 0x3
#define PL310_CACHE_TYPE 0x004
#define PL310_CTRL 0x100
#define CTRL_ENABLED 0x01
#define CTRL_DISABLED 0x00
#define PL310_AUX_CTRL 0x104
#define AUX_CTRL_MASK 0xc0000fff
#define AUX_CTRL_ASSOCIATIVITY_SHIFT 16
#define AUX_CTRL_WAY_SIZE_SHIFT 17
#define AUX_CTRL_WAY_SIZE_MASK (0x7 << 17)
#define AUX_CTRL_SHARE_OVERRIDE (1 << 22)
#define AUX_CTRL_NS_LOCKDOWN (1 << 26)
#define AUX_CTRL_NS_INT_CTRL (1 << 27)
#define AUX_CTRL_DATA_PREFETCH (1 << 28)
#define AUX_CTRL_INSTR_PREFETCH (1 << 29)
#define AUX_CTRL_EARLY_BRESP (1 << 30)
#define PL310_TAG_RAM_CTRL 0x108
#define PL310_DATA_RAM_CTRL 0x10C
#define RAM_CTRL_WRITE_SHIFT 8
#define RAM_CTRL_WRITE_MASK (0x7 << 8)
#define RAM_CTRL_READ_SHIFT 4
#define RAM_CTRL_READ_MASK (0x7 << 4)
#define RAM_CTRL_SETUP_SHIFT 0
#define RAM_CTRL_SETUP_MASK (0x7 << 0)
#define PL310_EVENT_COUNTER_CTRL 0x200
#define EVENT_COUNTER_CTRL_ENABLED (1 << 0)
#define EVENT_COUNTER_CTRL_C0_RESET (1 << 1)
#define EVENT_COUNTER_CTRL_C1_RESET (1 << 2)
#define PL310_EVENT_COUNTER1_CONF 0x204
#define PL310_EVENT_COUNTER0_CONF 0x208
#define EVENT_COUNTER_CONF_NOINTR 0
#define EVENT_COUNTER_CONF_INCR 1
#define EVENT_COUNTER_CONF_OVFW 2
#define EVENT_COUNTER_CONF_NOEV (0 << 2)
#define EVENT_COUNTER_CONF_CO (1 << 2)
#define EVENT_COUNTER_CONF_DRHIT (2 << 2)
#define EVENT_COUNTER_CONF_DRREQ (3 << 2)
#define EVENT_COUNTER_CONF_DWHIT (4 << 2)
#define EVENT_COUNTER_CONF_DWREQ (5 << 2)
#define EVENT_COUNTER_CONF_DWTREQ (6 << 2)
#define EVENT_COUNTER_CONF_DIRHIT (7 << 2)
#define EVENT_COUNTER_CONF_DIRREQ (8 << 2)
#define EVENT_COUNTER_CONF_WA (9 << 2)
#define PL310_EVENT_COUNTER1_VAL 0x20C
#define PL310_EVENT_COUNTER0_VAL 0x210
#define PL310_INTR_MASK 0x214
#define PL310_MASKED_INTR_STAT 0x218
#define PL310_RAW_INTR_STAT 0x21C
#define PL310_INTR_CLEAR 0x220
#define INTR_MASK_ALL ((1 << 9) - 1)
#define INTR_MASK_ECNTR (1 << 0)
#define INTR_MASK_PARRT (1 << 1)
#define INTR_MASK_PARRD (1 << 2)
#define INTR_MASK_ERRWT (1 << 3)
#define INTR_MASK_ERRWD (1 << 4)
#define INTR_MASK_ERRRT (1 << 5)
#define INTR_MASK_ERRRD (1 << 6)
#define INTR_MASK_SLVERR (1 << 7)
#define INTR_MASK_DECERR (1 << 8)
#define PL310_CACHE_SYNC 0x730
#define PL310_INV_LINE_PA 0x770
#define PL310_INV_WAY 0x77C
#define PL310_CLEAN_LINE_PA 0x7B0
#define PL310_CLEAN_LINE_IDX 0x7B8
#define PL310_CLEAN_WAY 0x7BC
#define PL310_CLEAN_INV_LINE_PA 0x7F0
#define PL310_CLEAN_INV_LINE_IDX 0x7F8
#define PL310_CLEAN_INV_WAY 0x7FC
#define PL310_LOCKDOWN_D_WAY(x) (0x900 + ((x) * 8))
#define PL310_LOCKDOWN_I_WAY(x) (0x904 + ((x) * 8))
#define PL310_LOCKDOWN_LINE_ENABLE 0x950
#define PL310_UNLOCK_ALL_LINES_WAY 0x954
#define PL310_ADDR_FILTER_STAR 0xC00
#define PL310_ADDR_FILTER_END 0xC04
#define PL310_DEBUG_CTRL 0xF40
#define DEBUG_CTRL_DISABLE_LINEFILL (1 << 0)
#define DEBUG_CTRL_DISABLE_WRITEBACK (1 << 1)
#define DEBUG_CTRL_SPNIDEN (1 << 2)
#define PL310_PREFETCH_CTRL 0xF60
#define PREFETCH_CTRL_OFFSET_MASK (0x1f)
#define PREFETCH_CTRL_NOTSAMEID (1 << 21)
#define PREFETCH_CTRL_INCR_DL (1 << 23)
#define PREFETCH_CTRL_PREFETCH_DROP (1 << 24)
#define PREFETCH_CTRL_DL_ON_WRAP (1 << 27)
#define PREFETCH_CTRL_DATA_PREFETCH (1 << 28)
#define PREFETCH_CTRL_INSTR_PREFETCH (1 << 29)
#define PREFETCH_CTRL_DL (1 << 30)
#define PL310_POWER_CTRL 0xF60
#define POWER_CTRL_ENABLE_GATING (1 << 0)
#define POWER_CTRL_ENABLE_STANDBY (1 << 1)
+struct intr_config_hook;
+
struct pl310_softc {
device_t sc_dev;
struct resource *sc_mem_res;
struct resource *sc_irq_res;
void* sc_irq_h;
int sc_enabled;
struct mtx sc_mtx;
u_int sc_rtl_revision;
+ struct intr_config_hook *sc_ich;
};
/**
* pl310_read4 - read a 32-bit value from the PL310 registers
* pl310_write4 - write a 32-bit value from the PL310 registers
* @off: byte offset within the register set to read from
* @val: the value to write into the register
*
*
* LOCKING:
* None
*
* RETURNS:
* nothing in case of write function, if read function returns the value read.
*/
static __inline uint32_t
pl310_read4(struct pl310_softc *sc, bus_size_t off)
{
return bus_read_4(sc->sc_mem_res, off);
}
static __inline void
pl310_write4(struct pl310_softc *sc, bus_size_t off, uint32_t val)
{
bus_write_4(sc->sc_mem_res, off, val);
}
void pl310_print_config(struct pl310_softc *sc);
void pl310_set_ram_latency(struct pl310_softc *sc, uint32_t which_reg,
uint32_t read, uint32_t write, uint32_t setup);
void platform_pl310_init(struct pl310_softc *);
void platform_pl310_write_ctrl(struct pl310_softc *, uint32_t);
void platform_pl310_write_debug(struct pl310_softc *, uint32_t);
#endif /* PL310_H_ */
Index: stable/10/sys/conf/options.arm
===================================================================
--- stable/10/sys/conf/options.arm (revision 270074)
+++ stable/10/sys/conf/options.arm (revision 270075)
@@ -1,62 +1,63 @@
#$FreeBSD$
ARM9_CACHE_WRITE_THROUGH opt_global.h
ARM_CACHE_LOCK_ENABLE opt_global.h
+ARM_DEVICE_MULTIPASS opt_global.h
ARM_KERN_DIRECTMAP opt_vm.h
ARM_L2_PIPT opt_global.h
ARM_MANY_BOARD opt_global.h
ARM_VFP_SUPPORT opt_global.h
ARM_WANT_TP_ADDRESS opt_global.h
COUNTS_PER_SEC opt_timer.h
CPU_ARM9 opt_global.h
CPU_ARM9E opt_global.h
CPU_ARM1136 opt_global.h
CPU_ARM1176 opt_global.h
CPU_CORTEXA opt_global.h
CPU_KRAIT opt_global.h
CPU_FA526 opt_global.h
CPU_FA626TE opt_global.h
CPU_MV_PJ4B opt_global.h
CPU_XSCALE_80219 opt_global.h
CPU_XSCALE_80321 opt_global.h
CPU_XSCALE_81342 opt_global.h
CPU_XSCALE_IXP425 opt_global.h
CPU_XSCALE_IXP435 opt_global.h
CPU_XSCALE_PXA2X0 opt_global.h
FLASHADDR opt_global.h
IPI_IRQ_START opt_smp.h
IPI_IRQ_END opt_smp.h
FREEBSD_BOOT_LOADER opt_global.h
IXP4XX_FLASH_SIZE opt_global.h
KERNPHYSADDR opt_global.h
KERNVIRTADDR opt_global.h
LINUX_BOOT_ABI opt_global.h
LOADERRAMADDR opt_global.h
PHYSADDR opt_global.h
SOCDEV_PA opt_global.h
SOCDEV_VA opt_global.h
PV_STATS opt_pmap.h
QEMU_WORKAROUNDS opt_global.h
SOC_MV_ARMADAXP opt_global.h
SOC_MV_DISCOVERY opt_global.h
SOC_MV_DOVE opt_global.h
SOC_MV_FREY opt_global.h
SOC_MV_KIRKWOOD opt_global.h
SOC_MV_LOKIPLUS opt_global.h
SOC_MV_ORION opt_global.h
SOC_OMAP3 opt_global.h
SOC_OMAP4 opt_global.h
SOC_TI_AM335X opt_global.h
SOC_TEGRA2 opt_global.h
XSCALE_CACHE_READ_WRITE_ALLOCATE opt_global.h
XSACLE_DISABLE_CCNT opt_timer.h
VERBOSE_INIT_ARM opt_global.h
VM_MAXUSER_ADDRESS opt_global.h
AT91_ATE_USE_RMII opt_at91.h
AT91_MCI_ALLOW_OVERCLOCK opt_at91.h
AT91_MCI_HAS_4WIRE opt_at91.h
AT91_MCI_SLOT_B opt_at91.h
GFB_DEBUG opt_gfb.h
GFB_NO_FONT_LOADING opt_gfb.h
GFB_NO_MODE_CHANGE opt_gfb.h
AT91C_MAIN_CLOCK opt_at91.h
VFP opt_global.h
Index: stable/10/sys/dev/fdt/simplebus.c
===================================================================
--- stable/10/sys/dev/fdt/simplebus.c (revision 270074)
+++ stable/10/sys/dev/fdt/simplebus.c (revision 270075)
@@ -1,435 +1,437 @@
/*-
* Copyright (c) 2013 Nathan Whitehorn
* 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 THE 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 THE 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct simplebus_range {
uint64_t bus;
uint64_t host;
uint64_t size;
};
struct simplebus_softc {
device_t dev;
phandle_t node;
struct simplebus_range *ranges;
int nranges;
pcell_t acells, scells;
};
struct simplebus_devinfo {
struct ofw_bus_devinfo obdinfo;
struct resource_list rl;
};
/*
* Bus interface.
*/
static int simplebus_probe(device_t dev);
static int simplebus_attach(device_t dev);
static struct resource *simplebus_alloc_resource(device_t, device_t, int,
int *, u_long, u_long, u_long, u_int);
static void simplebus_probe_nomatch(device_t bus, device_t child);
static int simplebus_print_child(device_t bus, device_t child);
/*
* ofw_bus interface
*/
static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus,
device_t child);
/*
* local methods
*/
static int simplebus_fill_ranges(phandle_t node,
struct simplebus_softc *sc);
static struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev,
phandle_t node);
/*
* Driver methods.
*/
static device_method_t simplebus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, simplebus_probe),
DEVMETHOD(device_attach, simplebus_attach),
/* Bus interface */
DEVMETHOD(bus_print_child, simplebus_print_child),
DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource),
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo),
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
DEVMETHOD_END
};
static driver_t simplebus_driver = {
"simplebus",
simplebus_methods,
sizeof(struct simplebus_softc)
};
static devclass_t simplebus_devclass;
-DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass, 0, 0);
-DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, 0);
+EARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass,
+ 0, 0, BUS_PASS_BUS);
+EARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass,
+ 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
static int
simplebus_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "simple-bus") &&
(ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev),
"soc") != 0))
return (ENXIO);
device_set_desc(dev, "Flattened device tree simple bus");
return (BUS_PROBE_GENERIC);
}
static int
simplebus_attach(device_t dev)
{
struct simplebus_softc *sc;
struct simplebus_devinfo *di;
phandle_t node;
device_t cdev;
node = ofw_bus_get_node(dev);
sc = device_get_softc(dev);
sc->dev = dev;
sc->node = node;
/*
* Some important numbers
*/
sc->acells = 2;
OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells));
sc->scells = 1;
OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells));
if (simplebus_fill_ranges(node, sc) < 0) {
device_printf(dev, "could not get ranges\n");
return (ENXIO);
}
/*
* In principle, simplebus could have an interrupt map, but ignore that
* for now
*/
for (node = OF_child(node); node > 0; node = OF_peer(node)) {
if ((di = simplebus_setup_dinfo(dev, node)) == NULL)
continue;
cdev = device_add_child(dev, NULL, -1);
if (cdev == NULL) {
device_printf(dev, "<%s>: device_add_child failed\n",
di->obdinfo.obd_name);
resource_list_free(&di->rl);
ofw_bus_gen_destroy_devinfo(&di->obdinfo);
free(di, M_DEVBUF);
continue;
}
device_set_ivars(cdev, di);
}
return (bus_generic_attach(dev));
}
static int
simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc)
{
int host_address_cells;
cell_t *base_ranges;
ssize_t nbase_ranges;
int err;
int i, j, k;
err = OF_searchencprop(OF_parent(node), "#address-cells",
&host_address_cells, sizeof(host_address_cells));
if (err <= 0)
return (-1);
nbase_ranges = OF_getproplen(node, "ranges");
if (nbase_ranges < 0)
return (-1);
sc->nranges = nbase_ranges / sizeof(cell_t) /
(sc->acells + host_address_cells + sc->scells);
if (sc->nranges == 0)
return (0);
sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
M_DEVBUF, M_WAITOK);
base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
for (i = 0, j = 0; i < sc->nranges; i++) {
sc->ranges[i].bus = 0;
for (k = 0; k < sc->acells; k++) {
sc->ranges[i].bus <<= 32;
sc->ranges[i].bus |= base_ranges[j++];
}
sc->ranges[i].host = 0;
for (k = 0; k < host_address_cells; k++) {
sc->ranges[i].host <<= 32;
sc->ranges[i].host |= base_ranges[j++];
}
sc->ranges[i].size = 0;
for (k = 0; k < sc->scells; k++) {
sc->ranges[i].size <<= 32;
sc->ranges[i].size |= base_ranges[j++];
}
}
free(base_ranges, M_DEVBUF);
return (sc->nranges);
}
static struct simplebus_devinfo *
simplebus_setup_dinfo(device_t dev, phandle_t node)
{
struct simplebus_softc *sc;
struct simplebus_devinfo *ndi;
uint32_t *reg, *intr, icells;
uint64_t phys, size;
phandle_t iparent;
int i, j, k;
int nintr;
int nreg;
sc = device_get_softc(dev);
ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
free(ndi, M_DEVBUF);
return (NULL);
}
resource_list_init(&ndi->rl);
nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®);
if (nreg == -1)
nreg = 0;
if (nreg % (sc->acells + sc->scells) != 0) {
if (bootverbose)
device_printf(dev, "Malformed reg property on <%s>\n",
ndi->obdinfo.obd_name);
nreg = 0;
}
for (i = 0, k = 0; i < nreg; i += sc->acells + sc->scells, k++) {
phys = size = 0;
for (j = 0; j < sc->acells; j++) {
phys <<= 32;
phys |= reg[i + j];
}
for (j = 0; j < sc->scells; j++) {
size <<= 32;
size |= reg[i + sc->acells + j];
}
resource_list_add(&ndi->rl, SYS_RES_MEMORY, k,
phys, phys + size - 1, size);
}
free(reg, M_OFWPROP);
nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr),
(void **)&intr);
if (nintr > 0) {
if (OF_searchencprop(node, "interrupt-parent", &iparent,
sizeof(iparent)) == -1) {
device_printf(dev, "No interrupt-parent found, "
"assuming direct parent\n");
iparent = OF_parent(node);
}
if (OF_searchencprop(OF_xref_phandle(iparent),
"#interrupt-cells", &icells, sizeof(icells)) == -1) {
device_printf(dev, "Missing #interrupt-cells property, "
"assuming <1>\n");
icells = 1;
}
if (icells < 1 || icells > nintr) {
device_printf(dev, "Invalid #interrupt-cells property "
"value <%d>, assuming <1>\n", icells);
icells = 1;
}
for (i = 0, k = 0; i < nintr; i += icells, k++) {
intr[i] = ofw_bus_map_intr(dev, iparent, icells,
&intr[i]);
resource_list_add(&ndi->rl, SYS_RES_IRQ, k, intr[i],
intr[i], 1);
}
free(intr, M_OFWPROP);
}
return (ndi);
}
static const struct ofw_bus_devinfo *
simplebus_get_devinfo(device_t bus __unused, device_t child)
{
struct simplebus_devinfo *ndi;
ndi = device_get_ivars(child);
return (&ndi->obdinfo);
}
static struct resource *
simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct simplebus_softc *sc;
struct simplebus_devinfo *di;
struct resource_list_entry *rle;
int j;
sc = device_get_softc(bus);
/*
* Request for the default allocation with a given rid: use resource
* list stored in the local device info.
*/
if ((start == 0UL) && (end == ~0UL)) {
if ((di = device_get_ivars(child)) == NULL)
return (NULL);
if (type == SYS_RES_IOPORT)
type = SYS_RES_MEMORY;
rle = resource_list_find(&di->rl, type, *rid);
if (rle == NULL) {
if (bootverbose)
device_printf(bus, "no default resources for "
"rid = %d, type = %d\n", *rid, type);
return (NULL);
}
start = rle->start;
end = rle->end;
count = rle->count;
}
if (type == SYS_RES_MEMORY) {
/* Remap through ranges property */
for (j = 0; j < sc->nranges; j++) {
if (start >= sc->ranges[j].bus && end <
sc->ranges[j].bus + sc->ranges[j].size) {
start -= sc->ranges[j].bus;
start += sc->ranges[j].host;
end -= sc->ranges[j].bus;
end += sc->ranges[j].host;
break;
}
}
if (j == sc->nranges && sc->nranges != 0) {
if (bootverbose)
device_printf(bus, "Could not map resource "
"%#lx-%#lx\n", start, end);
return (NULL);
}
}
return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
count, flags));
}
static int
simplebus_print_res(struct simplebus_devinfo *di)
{
int rv;
rv = 0;
rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx");
rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld");
return (rv);
}
static void
simplebus_probe_nomatch(device_t bus, device_t child)
{
const char *name, *type, *compat;
if (!bootverbose)
return;
name = ofw_bus_get_name(child);
type = ofw_bus_get_type(child);
compat = ofw_bus_get_compat(child);
device_printf(bus, "<%s>", name != NULL ? name : "unknown");
simplebus_print_res(device_get_ivars(child));
if (!ofw_bus_status_okay(child))
printf(" disabled");
if (type)
printf(" type %s", type);
if (compat)
printf(" compat %s", compat);
printf(" (no driver attached)\n");
}
static int
simplebus_print_child(device_t bus, device_t child)
{
int rv;
rv = bus_print_child_header(bus, child);
rv += simplebus_print_res(device_get_ivars(child));
if (!ofw_bus_status_okay(child))
rv += printf(" disabled");
rv += bus_print_child_footer(bus, child);
return (rv);
}
Index: stable/10/sys/dev/ofw/ofwbus.c
===================================================================
--- stable/10/sys/dev/ofw/ofwbus.c (revision 270074)
+++ stable/10/sys/dev/ofw/ofwbus.c (revision 270075)
@@ -1,527 +1,528 @@
/*-
* Copyright 1998 Massachusetts Institute of Technology
* Copyright 2001 by Thomas Moestl .
* Copyright 2006 by Marius Strobl .
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that both the above copyright notice and this
* permission notice appear in all copies, that both the above
* copyright notice and this permission notice appear in all
* supporting documentation, and that the name of M.I.T. not be used
* in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission. M.I.T. makes
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
* SHALL M.I.T. 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.
*
* from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* The ofwbus (which is a pseudo-bus actually) iterates over the nodes that
* hang from the Open Firmware root node and adds them as devices to this bus
* (except some special nodes which are excluded) so that drivers can be
* attached to them.
*
*/
struct ofwbus_devinfo {
struct ofw_bus_devinfo ndi_obdinfo;
struct resource_list ndi_rl;
};
struct ofwbus_softc {
uint32_t acells, scells;
struct rman sc_intr_rman;
struct rman sc_mem_rman;
};
static device_identify_t ofwbus_identify;
static device_probe_t ofwbus_probe;
static device_attach_t ofwbus_attach;
static bus_print_child_t ofwbus_print_child;
static bus_add_child_t ofwbus_add_child;
static bus_probe_nomatch_t ofwbus_probe_nomatch;
static bus_alloc_resource_t ofwbus_alloc_resource;
static bus_adjust_resource_t ofwbus_adjust_resource;
static bus_release_resource_t ofwbus_release_resource;
static bus_get_resource_list_t ofwbus_get_resource_list;
static ofw_bus_get_devinfo_t ofwbus_get_devinfo;
static int ofwbus_inlist(const char *, const char *const *);
static struct ofwbus_devinfo * ofwbus_setup_dinfo(device_t, phandle_t);
static void ofwbus_destroy_dinfo(struct ofwbus_devinfo *);
static int ofwbus_print_res(struct ofwbus_devinfo *);
static device_method_t ofwbus_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, ofwbus_identify),
DEVMETHOD(device_probe, ofwbus_probe),
DEVMETHOD(device_attach, ofwbus_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, ofwbus_print_child),
DEVMETHOD(bus_probe_nomatch, ofwbus_probe_nomatch),
DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
DEVMETHOD(bus_add_child, ofwbus_add_child),
DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
DEVMETHOD(bus_alloc_resource, ofwbus_alloc_resource),
DEVMETHOD(bus_adjust_resource, ofwbus_adjust_resource),
DEVMETHOD(bus_release_resource, ofwbus_release_resource),
DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
DEVMETHOD(bus_get_resource_list, ofwbus_get_resource_list),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_config_intr, bus_generic_config_intr),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_devinfo, ofwbus_get_devinfo),
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
DEVMETHOD_END
};
static driver_t ofwbus_driver = {
"ofwbus",
ofwbus_methods,
sizeof(struct ofwbus_softc)
};
static devclass_t ofwbus_devclass;
-DRIVER_MODULE(ofwbus, nexus, ofwbus_driver, ofwbus_devclass, 0, 0);
+EARLY_DRIVER_MODULE(ofwbus, nexus, ofwbus_driver, ofwbus_devclass, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(ofwbus, 1);
static const char *const ofwbus_excl_name[] = {
"FJSV,system",
"aliases",
"associations",
"chosen",
"cmp",
"counter-timer", /* No separate device; handled by psycho/sbus */
"failsafe",
"memory",
"openprom",
"options",
"packages",
"physical-memory",
"rsc",
"sgcn",
"todsg",
"virtual-memory",
NULL
};
static const char *const ofwbus_excl_type[] = {
"core",
"cpu",
NULL
};
static int
ofwbus_inlist(const char *name, const char *const *list)
{
int i;
if (name == NULL)
return (0);
for (i = 0; list[i] != NULL; i++)
if (strcmp(name, list[i]) == 0)
return (1);
return (0);
}
#define OFWBUS_EXCLUDED(name, type) \
(ofwbus_inlist((name), ofwbus_excl_name) || \
((type) != NULL && ofwbus_inlist((type), ofwbus_excl_type)))
static void
ofwbus_identify(driver_t *driver, device_t parent)
{
/* Check if Open Firmware has been instantiated */
if (OF_peer(0) == -1)
return;
if (device_find_child(parent, "ofwbus", -1) == NULL)
BUS_ADD_CHILD(parent, 0, "ofwbus", -1);
}
static int
ofwbus_probe(device_t dev)
{
device_set_desc(dev, "Open Firmware Device Tree");
return (BUS_PROBE_NOWILDCARD);
}
static int
ofwbus_attach(device_t dev)
{
struct ofwbus_devinfo *ndi;
struct ofwbus_softc *sc;
device_t cdev;
phandle_t node;
sc = device_get_softc(dev);
node = OF_peer(0);
/*
* If no Open Firmware, bail early
*/
if (node == -1)
return (ENXIO);
sc->sc_intr_rman.rm_type = RMAN_ARRAY;
sc->sc_intr_rman.rm_descr = "Interrupts";
sc->sc_mem_rman.rm_type = RMAN_ARRAY;
sc->sc_mem_rman.rm_descr = "Device Memory";
if (rman_init(&sc->sc_intr_rman) != 0 ||
rman_init(&sc->sc_mem_rman) != 0 ||
rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0 ||
rman_manage_region(&sc->sc_mem_rman, 0, BUS_SPACE_MAXADDR) != 0)
panic("%s: failed to set up rmans.", __func__);
/*
* Allow devices to identify.
*/
bus_generic_probe(dev);
/*
* Some important numbers
*/
sc->acells = 2;
OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells));
sc->scells = 1;
OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells));
/*
* Now walk the OFW tree and attach top-level devices.
*/
for (node = OF_child(node); node > 0; node = OF_peer(node)) {
if ((ndi = ofwbus_setup_dinfo(dev, node)) == NULL)
continue;
cdev = device_add_child(dev, NULL, -1);
if (cdev == NULL) {
device_printf(dev, "<%s>: device_add_child failed\n",
ndi->ndi_obdinfo.obd_name);
ofwbus_destroy_dinfo(ndi);
continue;
}
device_set_ivars(cdev, ndi);
}
return (bus_generic_attach(dev));
}
static device_t
ofwbus_add_child(device_t dev, u_int order, const char *name, int unit)
{
device_t cdev;
struct ofwbus_devinfo *ndi;
cdev = device_add_child_ordered(dev, order, name, unit);
if (cdev == NULL)
return (NULL);
ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
ndi->ndi_obdinfo.obd_node = -1;
resource_list_init(&ndi->ndi_rl);
device_set_ivars(cdev, ndi);
return (cdev);
}
static int
ofwbus_print_child(device_t bus, device_t child)
{
int rv;
rv = bus_print_child_header(bus, child);
rv += ofwbus_print_res(device_get_ivars(child));
rv += bus_print_child_footer(bus, child);
return (rv);
}
static void
ofwbus_probe_nomatch(device_t bus, device_t child)
{
const char *name, *type;
if (!bootverbose)
return;
name = ofw_bus_get_name(child);
type = ofw_bus_get_type(child);
device_printf(bus, "<%s>",
name != NULL ? name : "unknown");
ofwbus_print_res(device_get_ivars(child));
printf(" type %s (no driver attached)\n",
type != NULL ? type : "unknown");
}
static struct resource *
ofwbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct ofwbus_softc *sc;
struct rman *rm;
struct resource *rv;
struct resource_list_entry *rle;
int isdefault, passthrough;
isdefault = (start == 0UL && end == ~0UL);
passthrough = (device_get_parent(child) != bus);
sc = device_get_softc(bus);
rle = NULL;
if (!passthrough && isdefault) {
rle = resource_list_find(BUS_GET_RESOURCE_LIST(bus, child),
type, *rid);
if (rle == NULL)
return (NULL);
if (rle->res != NULL)
panic("%s: resource entry is busy", __func__);
start = rle->start;
count = ulmax(count, rle->count);
end = ulmax(rle->end, start + count - 1);
}
switch (type) {
case SYS_RES_IRQ:
rm = &sc->sc_intr_rman;
break;
case SYS_RES_MEMORY:
rm = &sc->sc_mem_rman;
break;
default:
return (NULL);
}
rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
child);
if (rv == NULL)
return (NULL);
rman_set_rid(rv, *rid);
if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type,
*rid, rv) != 0) {
rman_release_resource(rv);
return (NULL);
}
if (!passthrough && rle != NULL) {
rle->res = rv;
rle->start = rman_get_start(rv);
rle->end = rman_get_end(rv);
rle->count = rle->end - rle->start + 1;
}
return (rv);
}
static int
ofwbus_adjust_resource(device_t bus, device_t child __unused, int type,
struct resource *r, u_long start, u_long end)
{
struct ofwbus_softc *sc;
struct rman *rm;
device_t ofwbus;
ofwbus = bus;
while (strcmp(device_get_name(device_get_parent(ofwbus)), "root") != 0)
ofwbus = device_get_parent(ofwbus);
sc = device_get_softc(ofwbus);
switch (type) {
case SYS_RES_IRQ:
rm = &sc->sc_intr_rman;
break;
case SYS_RES_MEMORY:
rm = &sc->sc_mem_rman;
break;
default:
return (EINVAL);
}
if (rm == NULL)
return (ENXIO);
if (rman_is_region_manager(r, rm) == 0)
return (EINVAL);
return (rman_adjust_resource(r, start, end));
}
static int
ofwbus_release_resource(device_t bus __unused, device_t child, int type,
int rid, struct resource *r)
{
int error;
if ((rman_get_flags(r) & RF_ACTIVE) != 0) {
error = bus_deactivate_resource(child, type, rid, r);
if (error)
return (error);
}
return (rman_release_resource(r));
}
static struct resource_list *
ofwbus_get_resource_list(device_t bus __unused, device_t child)
{
struct ofwbus_devinfo *ndi;
ndi = device_get_ivars(child);
return (&ndi->ndi_rl);
}
static const struct ofw_bus_devinfo *
ofwbus_get_devinfo(device_t bus __unused, device_t child)
{
struct ofwbus_devinfo *ndi;
ndi = device_get_ivars(child);
return (&ndi->ndi_obdinfo);
}
static struct ofwbus_devinfo *
ofwbus_setup_dinfo(device_t dev, phandle_t node)
{
struct ofwbus_softc *sc;
struct ofwbus_devinfo *ndi;
uint32_t *reg, *intr, icells;
uint64_t phys, size;
phandle_t iparent;
int i, j;
int nintr;
int nreg;
sc = device_get_softc(dev);
ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
if (ofw_bus_gen_setup_devinfo(&ndi->ndi_obdinfo, node) != 0) {
free(ndi, M_DEVBUF);
return (NULL);
}
if (OFWBUS_EXCLUDED(ndi->ndi_obdinfo.obd_name,
ndi->ndi_obdinfo.obd_type)) {
ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo);
free(ndi, M_DEVBUF);
return (NULL);
}
resource_list_init(&ndi->ndi_rl);
nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®);
if (nreg == -1)
nreg = 0;
if (nreg % (sc->acells + sc->scells) != 0) {
if (bootverbose)
device_printf(dev, "Malformed reg property on <%s>\n",
ndi->ndi_obdinfo.obd_name);
nreg = 0;
}
for (i = 0; i < nreg; i += sc->acells + sc->scells) {
phys = size = 0;
for (j = 0; j < sc->acells; j++) {
phys <<= 32;
phys |= reg[i + j];
}
for (j = 0; j < sc->scells; j++) {
size <<= 32;
size |= reg[i + sc->acells + j];
}
/* Skip the dummy reg property of glue devices like ssm(4). */
if (size != 0)
resource_list_add(&ndi->ndi_rl, SYS_RES_MEMORY, i,
phys, phys + size - 1, size);
}
free(reg, M_OFWPROP);
nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr),
(void **)&intr);
if (nintr > 0) {
iparent = 0;
OF_searchencprop(node, "interrupt-parent", &iparent,
sizeof(iparent));
OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells",
&icells, sizeof(icells));
for (i = 0; i < nintr; i+= icells) {
intr[i] = ofw_bus_map_intr(dev, iparent, icells,
&intr[i]);
resource_list_add(&ndi->ndi_rl, SYS_RES_IRQ, i, intr[i],
intr[i], 1);
}
free(intr, M_OFWPROP);
}
return (ndi);
}
static void
ofwbus_destroy_dinfo(struct ofwbus_devinfo *ndi)
{
resource_list_free(&ndi->ndi_rl);
ofw_bus_gen_destroy_devinfo(&ndi->ndi_obdinfo);
free(ndi, M_DEVBUF);
}
static int
ofwbus_print_res(struct ofwbus_devinfo *ndi)
{
int rv;
rv = 0;
rv += resource_list_print_type(&ndi->ndi_rl, "mem", SYS_RES_MEMORY,
"%#lx");
rv += resource_list_print_type(&ndi->ndi_rl, "irq", SYS_RES_IRQ,
"%ld");
return (rv);
}
Index: stable/10/sys/sys/bus.h
===================================================================
--- stable/10/sys/sys/bus.h (revision 270074)
+++ stable/10/sys/sys/bus.h (revision 270075)
@@ -1,793 +1,799 @@
/*-
* Copyright (c) 1997,1998,2003 Doug Rabson
* 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 THE 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 THE 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.
*
* $FreeBSD$
*/
#ifndef _SYS_BUS_H_
#define _SYS_BUS_H_
#include
#include
/**
* @defgroup NEWBUS newbus - a generic framework for managing devices
* @{
*/
/**
* @brief Interface information structure.
*/
struct u_businfo {
int ub_version; /**< @brief interface version */
#define BUS_USER_VERSION 1
int ub_generation; /**< @brief generation count */
};
/**
* @brief State of the device.
*/
typedef enum device_state {
DS_NOTPRESENT = 10, /**< @brief not probed or probe failed */
DS_ALIVE = 20, /**< @brief probe succeeded */
DS_ATTACHING = 25, /**< @brief currently attaching */
DS_ATTACHED = 30, /**< @brief attach method called */
DS_BUSY = 40 /**< @brief device is open */
} device_state_t;
/**
* @brief Device information exported to userspace.
*/
struct u_device {
uintptr_t dv_handle;
uintptr_t dv_parent;
char dv_name[32]; /**< @brief Name of device in tree. */
char dv_desc[32]; /**< @brief Driver description */
char dv_drivername[32]; /**< @brief Driver name */
char dv_pnpinfo[128]; /**< @brief Plug and play info */
char dv_location[128]; /**< @brief Where is the device? */
uint32_t dv_devflags; /**< @brief API Flags for device */
uint16_t dv_flags; /**< @brief flags for dev date */
device_state_t dv_state; /**< @brief State of attachment */
/* XXX more driver info? */
};
#ifdef _KERNEL
#include
#include
/**
* devctl hooks. Typically one should use the devctl_notify
* hook to send the message. However, devctl_queue_data is also
* included in case devctl_notify isn't sufficiently general.
*/
boolean_t devctl_process_running(void);
void devctl_notify_f(const char *__system, const char *__subsystem,
const char *__type, const char *__data, int __flags);
void devctl_notify(const char *__system, const char *__subsystem,
const char *__type, const char *__data);
void devctl_queue_data_f(char *__data, int __flags);
void devctl_queue_data(char *__data);
/**
* @brief A device driver (included mainly for compatibility with
* FreeBSD 4.x).
*/
typedef struct kobj_class driver_t;
/**
* @brief A device class
*
* The devclass object has two main functions in the system. The first
* is to manage the allocation of unit numbers for device instances
* and the second is to hold the list of device drivers for a
* particular bus type. Each devclass has a name and there cannot be
* two devclasses with the same name. This ensures that unique unit
* numbers are allocated to device instances.
*
* Drivers that support several different bus attachments (e.g. isa,
* pci, pccard) should all use the same devclass to ensure that unit
* numbers do not conflict.
*
* Each devclass may also have a parent devclass. This is used when
* searching for device drivers to allow a form of inheritance. When
* matching drivers with devices, first the driver list of the parent
* device's devclass is searched. If no driver is found in that list,
* the search continues in the parent devclass (if any).
*/
typedef struct devclass *devclass_t;
/**
* @brief A device method (included mainly for compatibility with
* FreeBSD 4.x).
*/
#define device_method_t kobj_method_t
/**
* @brief Driver interrupt filter return values
*
* If a driver provides an interrupt filter routine it must return an
* integer consisting of oring together zero or more of the following
* flags:
*
* FILTER_STRAY - this device did not trigger the interrupt
* FILTER_HANDLED - the interrupt has been fully handled and can be EOId
* FILTER_SCHEDULE_THREAD - the threaded interrupt handler should be
* scheduled to execute
*
* If the driver does not provide a filter, then the interrupt code will
* act is if the filter had returned FILTER_SCHEDULE_THREAD. Note that it
* is illegal to specify any other flag with FILTER_STRAY and that it is
* illegal to not specify either of FILTER_HANDLED or FILTER_SCHEDULE_THREAD
* if FILTER_STRAY is not specified.
*/
#define FILTER_STRAY 0x01
#define FILTER_HANDLED 0x02
#define FILTER_SCHEDULE_THREAD 0x04
/**
* @brief Driver interrupt service routines
*
* The filter routine is run in primary interrupt context and may not
* block or use regular mutexes. It may only use spin mutexes for
* synchronization. The filter may either completely handle the
* interrupt or it may perform some of the work and defer more
* expensive work to the regular interrupt handler. If a filter
* routine is not registered by the driver, then the regular interrupt
* handler is always used to handle interrupts from this device.
*
* The regular interrupt handler executes in its own thread context
* and may use regular mutexes. However, it is prohibited from
* sleeping on a sleep queue.
*/
typedef int driver_filter_t(void*);
typedef void driver_intr_t(void*);
/**
* @brief Interrupt type bits.
*
* These flags are used both by newbus interrupt
* registration (nexus.c) and also in struct intrec, which defines
* interrupt properties.
*
* XXX We should probably revisit this and remove the vestiges of the
* spls implicit in names like INTR_TYPE_TTY. In the meantime, don't
* confuse things by renaming them (Grog, 18 July 2000).
*
* Buses which do interrupt remapping will want to change their type
* to reflect what sort of devices are underneath.
*/
enum intr_type {
INTR_TYPE_TTY = 1,
INTR_TYPE_BIO = 2,
INTR_TYPE_NET = 4,
INTR_TYPE_CAM = 8,
INTR_TYPE_MISC = 16,
INTR_TYPE_CLK = 32,
INTR_TYPE_AV = 64,
INTR_EXCL = 256, /* exclusive interrupt */
INTR_MPSAFE = 512, /* this interrupt is SMP safe */
INTR_ENTROPY = 1024, /* this interrupt provides entropy */
INTR_MD1 = 4096, /* flag reserved for MD use */
INTR_MD2 = 8192, /* flag reserved for MD use */
INTR_MD3 = 16384, /* flag reserved for MD use */
INTR_MD4 = 32768 /* flag reserved for MD use */
};
enum intr_trigger {
INTR_TRIGGER_CONFORM = 0,
INTR_TRIGGER_EDGE = 1,
INTR_TRIGGER_LEVEL = 2
};
enum intr_polarity {
INTR_POLARITY_CONFORM = 0,
INTR_POLARITY_HIGH = 1,
INTR_POLARITY_LOW = 2
};
typedef int (*devop_t)(void);
/**
* @brief This structure is deprecated.
*
* Use the kobj(9) macro DEFINE_CLASS to
* declare classes which implement device drivers.
*/
struct driver {
KOBJ_CLASS_FIELDS;
};
/*
* Definitions for drivers which need to keep simple lists of resources
* for their child devices.
*/
struct resource;
/**
* @brief An entry for a single resource in a resource list.
*/
struct resource_list_entry {
STAILQ_ENTRY(resource_list_entry) link;
int type; /**< @brief type argument to alloc_resource */
int rid; /**< @brief resource identifier */
int flags; /**< @brief resource flags */
struct resource *res; /**< @brief the real resource when allocated */
u_long start; /**< @brief start of resource range */
u_long end; /**< @brief end of resource range */
u_long count; /**< @brief count within range */
};
STAILQ_HEAD(resource_list, resource_list_entry);
#define RLE_RESERVED 0x0001 /* Reserved by the parent bus. */
#define RLE_ALLOCATED 0x0002 /* Reserved resource is allocated. */
#define RLE_PREFETCH 0x0004 /* Resource is a prefetch range. */
void resource_list_init(struct resource_list *rl);
void resource_list_free(struct resource_list *rl);
struct resource_list_entry *
resource_list_add(struct resource_list *rl,
int type, int rid,
u_long start, u_long end, u_long count);
int resource_list_add_next(struct resource_list *rl,
int type,
u_long start, u_long end, u_long count);
int resource_list_busy(struct resource_list *rl,
int type, int rid);
int resource_list_reserved(struct resource_list *rl, int type, int rid);
struct resource_list_entry*
resource_list_find(struct resource_list *rl,
int type, int rid);
void resource_list_delete(struct resource_list *rl,
int type, int rid);
struct resource *
resource_list_alloc(struct resource_list *rl,
device_t bus, device_t child,
int type, int *rid,
u_long start, u_long end,
u_long count, u_int flags);
int resource_list_release(struct resource_list *rl,
device_t bus, device_t child,
int type, int rid, struct resource *res);
int resource_list_release_active(struct resource_list *rl,
device_t bus, device_t child,
int type);
struct resource *
resource_list_reserve(struct resource_list *rl,
device_t bus, device_t child,
int type, int *rid,
u_long start, u_long end,
u_long count, u_int flags);
int resource_list_unreserve(struct resource_list *rl,
device_t bus, device_t child,
int type, int rid);
void resource_list_purge(struct resource_list *rl);
int resource_list_print_type(struct resource_list *rl,
const char *name, int type,
const char *format);
/*
* The root bus, to which all top-level busses are attached.
*/
extern device_t root_bus;
extern devclass_t root_devclass;
void root_bus_configure(void);
/*
* Useful functions for implementing busses.
*/
int bus_generic_activate_resource(device_t dev, device_t child, int type,
int rid, struct resource *r);
device_t
bus_generic_add_child(device_t dev, u_int order, const char *name,
int unit);
int bus_generic_adjust_resource(device_t bus, device_t child, int type,
struct resource *r, u_long start,
u_long end);
struct resource *
bus_generic_alloc_resource(device_t bus, device_t child, int type,
int *rid, u_long start, u_long end,
u_long count, u_int flags);
int bus_generic_attach(device_t dev);
int bus_generic_bind_intr(device_t dev, device_t child,
struct resource *irq, int cpu);
int bus_generic_child_present(device_t dev, device_t child);
int bus_generic_config_intr(device_t, int, enum intr_trigger,
enum intr_polarity);
int bus_generic_describe_intr(device_t dev, device_t child,
struct resource *irq, void *cookie,
const char *descr);
int bus_generic_deactivate_resource(device_t dev, device_t child, int type,
int rid, struct resource *r);
int bus_generic_detach(device_t dev);
void bus_generic_driver_added(device_t dev, driver_t *driver);
bus_dma_tag_t
bus_generic_get_dma_tag(device_t dev, device_t child);
struct resource_list *
bus_generic_get_resource_list (device_t, device_t);
void bus_generic_new_pass(device_t dev);
int bus_print_child_header(device_t dev, device_t child);
int bus_print_child_footer(device_t dev, device_t child);
int bus_generic_print_child(device_t dev, device_t child);
int bus_generic_probe(device_t dev);
int bus_generic_read_ivar(device_t dev, device_t child, int which,
uintptr_t *result);
int bus_generic_release_resource(device_t bus, device_t child,
int type, int rid, struct resource *r);
int bus_generic_resume(device_t dev);
int bus_generic_setup_intr(device_t dev, device_t child,
struct resource *irq, int flags,
driver_filter_t *filter, driver_intr_t *intr,
void *arg, void **cookiep);
struct resource *
bus_generic_rl_alloc_resource (device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
void bus_generic_rl_delete_resource (device_t, device_t, int, int);
int bus_generic_rl_get_resource (device_t, device_t, int, int, u_long *,
u_long *);
int bus_generic_rl_set_resource (device_t, device_t, int, int, u_long,
u_long);
int bus_generic_rl_release_resource (device_t, device_t, int, int,
struct resource *);
int bus_generic_shutdown(device_t dev);
int bus_generic_suspend(device_t dev);
int bus_generic_teardown_intr(device_t dev, device_t child,
struct resource *irq, void *cookie);
int bus_generic_write_ivar(device_t dev, device_t child, int which,
uintptr_t value);
/*
* Wrapper functions for the BUS_*_RESOURCE methods to make client code
* a little simpler.
*/
struct resource_spec {
int type;
int rid;
int flags;
};
int bus_alloc_resources(device_t dev, struct resource_spec *rs,
struct resource **res);
void bus_release_resources(device_t dev, const struct resource_spec *rs,
struct resource **res);
int bus_adjust_resource(device_t child, int type, struct resource *r,
u_long start, u_long end);
struct resource *bus_alloc_resource(device_t dev, int type, int *rid,
u_long start, u_long end, u_long count,
u_int flags);
int bus_activate_resource(device_t dev, int type, int rid,
struct resource *r);
int bus_deactivate_resource(device_t dev, int type, int rid,
struct resource *r);
bus_dma_tag_t bus_get_dma_tag(device_t dev);
int bus_release_resource(device_t dev, int type, int rid,
struct resource *r);
int bus_free_resource(device_t dev, int type, struct resource *r);
int bus_setup_intr(device_t dev, struct resource *r, int flags,
driver_filter_t filter, driver_intr_t handler,
void *arg, void **cookiep);
int bus_teardown_intr(device_t dev, struct resource *r, void *cookie);
int bus_bind_intr(device_t dev, struct resource *r, int cpu);
int bus_describe_intr(device_t dev, struct resource *irq, void *cookie,
const char *fmt, ...);
int bus_set_resource(device_t dev, int type, int rid,
u_long start, u_long count);
int bus_get_resource(device_t dev, int type, int rid,
u_long *startp, u_long *countp);
u_long bus_get_resource_start(device_t dev, int type, int rid);
u_long bus_get_resource_count(device_t dev, int type, int rid);
void bus_delete_resource(device_t dev, int type, int rid);
int bus_child_present(device_t child);
int bus_child_pnpinfo_str(device_t child, char *buf, size_t buflen);
int bus_child_location_str(device_t child, char *buf, size_t buflen);
void bus_enumerate_hinted_children(device_t bus);
static __inline struct resource *
bus_alloc_resource_any(device_t dev, int type, int *rid, u_int flags)
{
return (bus_alloc_resource(dev, type, rid, 0ul, ~0ul, 1, flags));
}
/*
* Access functions for device.
*/
device_t device_add_child(device_t dev, const char *name, int unit);
device_t device_add_child_ordered(device_t dev, u_int order,
const char *name, int unit);
void device_busy(device_t dev);
int device_delete_child(device_t dev, device_t child);
int device_delete_children(device_t dev);
int device_attach(device_t dev);
int device_detach(device_t dev);
void device_disable(device_t dev);
void device_enable(device_t dev);
device_t device_find_child(device_t dev, const char *classname,
int unit);
const char *device_get_desc(device_t dev);
devclass_t device_get_devclass(device_t dev);
driver_t *device_get_driver(device_t dev);
u_int32_t device_get_flags(device_t dev);
device_t device_get_parent(device_t dev);
int device_get_children(device_t dev, device_t **listp, int *countp);
void *device_get_ivars(device_t dev);
void device_set_ivars(device_t dev, void *ivars);
const char *device_get_name(device_t dev);
const char *device_get_nameunit(device_t dev);
void *device_get_softc(device_t dev);
device_state_t device_get_state(device_t dev);
int device_get_unit(device_t dev);
struct sysctl_ctx_list *device_get_sysctl_ctx(device_t dev);
struct sysctl_oid *device_get_sysctl_tree(device_t dev);
int device_is_alive(device_t dev); /* did probe succeed? */
int device_is_attached(device_t dev); /* did attach succeed? */
int device_is_enabled(device_t dev);
int device_is_quiet(device_t dev);
int device_print_prettyname(device_t dev);
int device_printf(device_t dev, const char *, ...) __printflike(2, 3);
int device_probe(device_t dev);
int device_probe_and_attach(device_t dev);
int device_probe_child(device_t bus, device_t dev);
int device_quiesce(device_t dev);
void device_quiet(device_t dev);
void device_set_desc(device_t dev, const char* desc);
void device_set_desc_copy(device_t dev, const char* desc);
int device_set_devclass(device_t dev, const char *classname);
int device_set_driver(device_t dev, driver_t *driver);
void device_set_flags(device_t dev, u_int32_t flags);
void device_set_softc(device_t dev, void *softc);
void device_free_softc(void *softc);
void device_claim_softc(device_t dev);
int device_set_unit(device_t dev, int unit); /* XXX DONT USE XXX */
int device_shutdown(device_t dev);
void device_unbusy(device_t dev);
void device_verbose(device_t dev);
/*
* Access functions for devclass.
*/
int devclass_add_driver(devclass_t dc, driver_t *driver,
int pass, devclass_t *dcp);
devclass_t devclass_create(const char *classname);
int devclass_delete_driver(devclass_t busclass, driver_t *driver);
devclass_t devclass_find(const char *classname);
const char *devclass_get_name(devclass_t dc);
device_t devclass_get_device(devclass_t dc, int unit);
void *devclass_get_softc(devclass_t dc, int unit);
int devclass_get_devices(devclass_t dc, device_t **listp, int *countp);
int devclass_get_drivers(devclass_t dc, driver_t ***listp, int *countp);
int devclass_get_count(devclass_t dc);
int devclass_get_maxunit(devclass_t dc);
int devclass_find_free_unit(devclass_t dc, int unit);
void devclass_set_parent(devclass_t dc, devclass_t pdc);
devclass_t devclass_get_parent(devclass_t dc);
struct sysctl_ctx_list *devclass_get_sysctl_ctx(devclass_t dc);
struct sysctl_oid *devclass_get_sysctl_tree(devclass_t dc);
/*
* Access functions for device resources.
*/
int resource_int_value(const char *name, int unit, const char *resname,
int *result);
int resource_long_value(const char *name, int unit, const char *resname,
long *result);
int resource_string_value(const char *name, int unit, const char *resname,
const char **result);
int resource_disabled(const char *name, int unit);
int resource_find_match(int *anchor, const char **name, int *unit,
const char *resname, const char *value);
int resource_find_dev(int *anchor, const char *name, int *unit,
const char *resname, const char *value);
int resource_set_int(const char *name, int unit, const char *resname,
int value);
int resource_set_long(const char *name, int unit, const char *resname,
long value);
int resource_set_string(const char *name, int unit, const char *resname,
const char *value);
/*
* Functions for maintaining and checking consistency of
* bus information exported to userspace.
*/
int bus_data_generation_check(int generation);
void bus_data_generation_update(void);
/**
* Some convenience defines for probe routines to return. These are just
* suggested values, and there's nothing magical about them.
* BUS_PROBE_SPECIFIC is for devices that cannot be reprobed, and that no
* possible other driver may exist (typically legacy drivers who don't fallow
* all the rules, or special needs drivers). BUS_PROBE_VENDOR is the
* suggested value that vendor supplied drivers use. This is for source or
* binary drivers that are not yet integrated into the FreeBSD tree. Its use
* in the base OS is prohibited. BUS_PROBE_DEFAULT is the normal return value
* for drivers to use. It is intended that nearly all of the drivers in the
* tree should return this value. BUS_PROBE_LOW_PRIORITY are for drivers that
* have special requirements like when there are two drivers that support
* overlapping series of hardware devices. In this case the one that supports
* the older part of the line would return this value, while the one that
* supports the newer ones would return BUS_PROBE_DEFAULT. BUS_PROBE_GENERIC
* is for drivers that wish to have a generic form and a specialized form,
* like is done with the pci bus and the acpi pci bus. BUS_PROBE_HOOVER is
* for those busses that implement a generic device place-holder for devices on
* the bus that have no more specific driver for them (aka ugen).
* BUS_PROBE_NOWILDCARD or lower means that the device isn't really bidding
* for a device node, but accepts only devices that its parent has told it
* use this driver.
*/
#define BUS_PROBE_SPECIFIC 0 /* Only I can use this device */
#define BUS_PROBE_VENDOR (-10) /* Vendor supplied driver */
#define BUS_PROBE_DEFAULT (-20) /* Base OS default driver */
#define BUS_PROBE_LOW_PRIORITY (-40) /* Older, less desirable drivers */
#define BUS_PROBE_GENERIC (-100) /* generic driver for dev */
#define BUS_PROBE_HOOVER (-500) /* Generic dev for all devs on bus */
#define BUS_PROBE_NOWILDCARD (-2000000000) /* No wildcard device matches */
/**
* During boot, the device tree is scanned multiple times. Each scan,
* or pass, drivers may be attached to devices. Each driver
* attachment is assigned a pass number. Drivers may only probe and
* attach to devices if their pass number is less than or equal to the
* current system-wide pass number. The default pass is the last pass
* and is used by most drivers. Drivers needed by the scheduler are
* probed in earlier passes.
*/
#define BUS_PASS_ROOT 0 /* Used to attach root0. */
#define BUS_PASS_BUS 10 /* Busses and bridges. */
#define BUS_PASS_CPU 20 /* CPU devices. */
#define BUS_PASS_RESOURCE 30 /* Resource discovery. */
#define BUS_PASS_INTERRUPT 40 /* Interrupt controllers. */
#define BUS_PASS_TIMER 50 /* Timers and clocks. */
#define BUS_PASS_SCHEDULER 60 /* Start scheduler. */
#define BUS_PASS_DEFAULT __INT_MAX /* Everything else. */
+#define BUS_PASS_ORDER_FIRST 0
+#define BUS_PASS_ORDER_EARLY 2
+#define BUS_PASS_ORDER_MIDDLE 5
+#define BUS_PASS_ORDER_LATE 7
+#define BUS_PASS_ORDER_LAST 9
+
extern int bus_current_pass;
void bus_set_pass(int pass);
/**
* Shorthands for constructing method tables.
*/
#define DEVMETHOD KOBJMETHOD
#define DEVMETHOD_END KOBJMETHOD_END
/*
* Some common device interfaces.
*/
#include "device_if.h"
#include "bus_if.h"
struct module;
int driver_module_handler(struct module *, int, void *);
/**
* Module support for automatically adding drivers to busses.
*/
struct driver_module_data {
int (*dmd_chainevh)(struct module *, int, void *);
void *dmd_chainarg;
const char *dmd_busname;
kobj_class_t dmd_driver;
devclass_t *dmd_devclass;
int dmd_pass;
};
#define EARLY_DRIVER_MODULE_ORDERED(name, busname, driver, devclass, \
evh, arg, order, pass) \
\
static struct driver_module_data name##_##busname##_driver_mod = { \
evh, arg, \
#busname, \
(kobj_class_t) &driver, \
&devclass, \
pass \
}; \
\
static moduledata_t name##_##busname##_mod = { \
#busname "/" #name, \
driver_module_handler, \
&name##_##busname##_driver_mod \
}; \
DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \
SI_SUB_DRIVERS, order)
#define EARLY_DRIVER_MODULE(name, busname, driver, devclass, evh, arg, pass) \
EARLY_DRIVER_MODULE_ORDERED(name, busname, driver, devclass, \
evh, arg, SI_ORDER_MIDDLE, pass)
#define DRIVER_MODULE_ORDERED(name, busname, driver, devclass, evh, arg,\
order) \
EARLY_DRIVER_MODULE_ORDERED(name, busname, driver, devclass, \
evh, arg, order, BUS_PASS_DEFAULT)
#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \
EARLY_DRIVER_MODULE(name, busname, driver, devclass, evh, arg, \
BUS_PASS_DEFAULT)
/**
* Generic ivar accessor generation macros for bus drivers
*/
#define __BUS_ACCESSOR(varp, var, ivarp, ivar, type) \
\
static __inline type varp ## _get_ ## var(device_t dev) \
{ \
uintptr_t v; \
BUS_READ_IVAR(device_get_parent(dev), dev, \
ivarp ## _IVAR_ ## ivar, &v); \
return ((type) v); \
} \
\
static __inline void varp ## _set_ ## var(device_t dev, type t) \
{ \
uintptr_t v = (uintptr_t) t; \
BUS_WRITE_IVAR(device_get_parent(dev), dev, \
ivarp ## _IVAR_ ## ivar, v); \
}
/**
* Shorthand macros, taking resource argument
* Generated with sys/tools/bus_macro.sh
*/
#define bus_barrier(r, o, l, f) \
bus_space_barrier((r)->r_bustag, (r)->r_bushandle, (o), (l), (f))
#define bus_read_1(r, o) \
bus_space_read_1((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_1(r, o, d, c) \
bus_space_read_multi_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_1(r, o, d, c) \
bus_space_read_region_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_1(r, o, v, c) \
bus_space_set_multi_1((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_1(r, o, v, c) \
bus_space_set_region_1((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_1(r, o, v) \
bus_space_write_1((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_1(r, o, d, c) \
bus_space_write_multi_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_1(r, o, d, c) \
bus_space_write_region_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_stream_1(r, o) \
bus_space_read_stream_1((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_stream_1(r, o, d, c) \
bus_space_read_multi_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_stream_1(r, o, d, c) \
bus_space_read_region_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_stream_1(r, o, v, c) \
bus_space_set_multi_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_stream_1(r, o, v, c) \
bus_space_set_region_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_stream_1(r, o, v) \
bus_space_write_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_stream_1(r, o, d, c) \
bus_space_write_multi_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_stream_1(r, o, d, c) \
bus_space_write_region_stream_1((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_2(r, o) \
bus_space_read_2((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_2(r, o, d, c) \
bus_space_read_multi_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_2(r, o, d, c) \
bus_space_read_region_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_2(r, o, v, c) \
bus_space_set_multi_2((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_2(r, o, v, c) \
bus_space_set_region_2((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_2(r, o, v) \
bus_space_write_2((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_2(r, o, d, c) \
bus_space_write_multi_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_2(r, o, d, c) \
bus_space_write_region_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_stream_2(r, o) \
bus_space_read_stream_2((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_stream_2(r, o, d, c) \
bus_space_read_multi_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_stream_2(r, o, d, c) \
bus_space_read_region_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_stream_2(r, o, v, c) \
bus_space_set_multi_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_stream_2(r, o, v, c) \
bus_space_set_region_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_stream_2(r, o, v) \
bus_space_write_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_stream_2(r, o, d, c) \
bus_space_write_multi_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_stream_2(r, o, d, c) \
bus_space_write_region_stream_2((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_4(r, o) \
bus_space_read_4((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_4(r, o, d, c) \
bus_space_read_multi_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_4(r, o, d, c) \
bus_space_read_region_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_4(r, o, v, c) \
bus_space_set_multi_4((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_4(r, o, v, c) \
bus_space_set_region_4((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_4(r, o, v) \
bus_space_write_4((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_4(r, o, d, c) \
bus_space_write_multi_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_4(r, o, d, c) \
bus_space_write_region_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_stream_4(r, o) \
bus_space_read_stream_4((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_stream_4(r, o, d, c) \
bus_space_read_multi_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_stream_4(r, o, d, c) \
bus_space_read_region_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_stream_4(r, o, v, c) \
bus_space_set_multi_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_stream_4(r, o, v, c) \
bus_space_set_region_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_stream_4(r, o, v) \
bus_space_write_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_stream_4(r, o, d, c) \
bus_space_write_multi_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_stream_4(r, o, d, c) \
bus_space_write_region_stream_4((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_8(r, o) \
bus_space_read_8((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_8(r, o, d, c) \
bus_space_read_multi_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_8(r, o, d, c) \
bus_space_read_region_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_8(r, o, v, c) \
bus_space_set_multi_8((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_8(r, o, v, c) \
bus_space_set_region_8((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_8(r, o, v) \
bus_space_write_8((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_8(r, o, d, c) \
bus_space_write_multi_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_8(r, o, d, c) \
bus_space_write_region_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_stream_8(r, o) \
bus_space_read_stream_8((r)->r_bustag, (r)->r_bushandle, (o))
#define bus_read_multi_stream_8(r, o, d, c) \
bus_space_read_multi_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_read_region_stream_8(r, o, d, c) \
bus_space_read_region_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_set_multi_stream_8(r, o, v, c) \
bus_space_set_multi_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_set_region_stream_8(r, o, v, c) \
bus_space_set_region_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (v), (c))
#define bus_write_stream_8(r, o, v) \
bus_space_write_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (v))
#define bus_write_multi_stream_8(r, o, d, c) \
bus_space_write_multi_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#define bus_write_region_stream_8(r, o, d, c) \
bus_space_write_region_stream_8((r)->r_bustag, (r)->r_bushandle, (o), (d), (c))
#endif /* _KERNEL */
#endif /* !_SYS_BUS_H_ */
Index: stable/10
===================================================================
--- stable/10 (revision 270074)
+++ stable/10 (revision 270075)
Property changes on: stable/10
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head:r269594,269596-269598,269605-269606