o Revamp the sparc64 interrupt code in order to be able to interface
with the INTR_FILTER-enabled MI code. Basically this consists of registering an interrupt controller (of which there can be multiple and optionally different ones either per host-to-foo bridge or shared amongst host-to-foo bridges in any one machine) along with an interrupt vector as specific argument for all the interrupt vectors used by a given host-to-foo bridge (roughly similar to registering interrupt sources on amd64 and i386), providing functions to enable, clear and disable the interrupts of the children beneath the bridge. This also includes: - No longer entering a critical section in tl0_intr() and tl1_intr() for executing interrupt handlers but rather let the handlers enter it themselves so in the case of intr_event_handle() we don't enter a nested critical section. - Adding infrastructure for binding delivery of interrupt vectors to specific CPUs which later on can be interfaced with the code from amd64/i386 for binding interrupts to specific CPUs. - Getting rid of the wrapper hack introduced along the lines of the API changes for INTR_FILTER which as a side-effect caused interrupts associated with ithread handlers only to get the elevated priority of those associated with filters ("fast handlers") (this removes the hack also in the non-INTR_FILTER case). - Disabling (by not clearing) an interrupt in the interrupt controller until all associated handlers have been executed, which is crucial for the typical locking strategy of NIC drivers in order to work correctly in case of shared interrupts. This was a more or less theoretical problem on sparc64 though, as shared interrupts are rather uncommon there except for the on-board SCCs and UARTs. Note that due to the behavior of at least of some of the interrupt controllers used on sparc64 an enable+EOI instead of a disable+EOI approach (as implied by the INTR_FILTER MI code and implemented on other architectures) is used as the latter can cause lost interrupts or in the worst case interrupt starvation. o Correct a typo in sbus_alloc_resource() which caused (pass-through) allocations to only work down to the grandchildren of the bus, which wasn't a real problem so far as we don't support any devices which are great-grandchildren or greater of a U2S bridge, yet. o In fhc(4) use bus_{read,write}_4() instead of bus_space_{read,write}_4() in order to get rid of sc_bh and sc_bt in the fhc_softc. Also get rid of some other unneeded members in fhc_softc. Reviewed by: marcel (earlier version) Approved by: re (kensmith)
This commit is contained in:
parent
91c0813e7d
commit
7439368f60
@ -50,31 +50,17 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sparc64/fhc/fhcreg.h>
|
||||
#include <sparc64/sbus/ofw_sbus.h>
|
||||
|
||||
struct fhc_clr {
|
||||
driver_filter_t *fc_filter;
|
||||
driver_intr_t *fc_func;
|
||||
void *fc_arg;
|
||||
void *fc_cookie;
|
||||
bus_space_tag_t fc_bt;
|
||||
bus_space_handle_t fc_bh;
|
||||
};
|
||||
|
||||
struct fhc_devinfo {
|
||||
struct ofw_bus_devinfo fdi_obdinfo;
|
||||
struct resource_list fdi_rl;
|
||||
};
|
||||
|
||||
struct fhc_softc {
|
||||
struct resource * sc_memres[FHC_NREG];
|
||||
bus_space_handle_t sc_bh[FHC_NREG];
|
||||
bus_space_tag_t sc_bt[FHC_NREG];
|
||||
struct resource *sc_memres[FHC_NREG];
|
||||
int sc_nrange;
|
||||
struct sbus_ranges *sc_ranges;
|
||||
uint32_t sc_board;
|
||||
int sc_ign;
|
||||
struct cdev *sc_led_dev;
|
||||
int sc_flags;
|
||||
#define FHC_CENTRAL (1 << 0)
|
||||
};
|
||||
|
||||
static device_probe_t fhc_probe;
|
||||
@ -82,13 +68,13 @@ static device_attach_t fhc_attach;
|
||||
static bus_print_child_t fhc_print_child;
|
||||
static bus_probe_nomatch_t fhc_probe_nomatch;
|
||||
static bus_setup_intr_t fhc_setup_intr;
|
||||
static bus_teardown_intr_t fhc_teardown_intr;
|
||||
static bus_alloc_resource_t fhc_alloc_resource;
|
||||
static bus_get_resource_list_t fhc_get_resource_list;
|
||||
static ofw_bus_get_devinfo_t fhc_get_devinfo;
|
||||
|
||||
static driver_filter_t fhc_filter_stub;
|
||||
static driver_intr_t fhc_intr_stub;
|
||||
static void fhc_intr_enable(void *);
|
||||
static void fhc_intr_disable(void *);
|
||||
static void fhc_intr_eoi(void *);
|
||||
static void fhc_led_func(void *, int);
|
||||
static int fhc_print_res(struct fhc_devinfo *);
|
||||
|
||||
@ -104,7 +90,7 @@ static device_method_t fhc_methods[] = {
|
||||
DEVMETHOD(bus_print_child, fhc_print_child),
|
||||
DEVMETHOD(bus_probe_nomatch, fhc_probe_nomatch),
|
||||
DEVMETHOD(bus_setup_intr, fhc_setup_intr),
|
||||
DEVMETHOD(bus_teardown_intr, fhc_teardown_intr),
|
||||
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
|
||||
DEVMETHOD(bus_alloc_resource, fhc_alloc_resource),
|
||||
DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
@ -134,6 +120,17 @@ static devclass_t fhc_devclass;
|
||||
DRIVER_MODULE(fhc, central, fhc_driver, fhc_devclass, 0, 0);
|
||||
DRIVER_MODULE(fhc, nexus, fhc_driver, fhc_devclass, 0, 0);
|
||||
|
||||
static const struct intr_controller fhc_ic = {
|
||||
fhc_intr_enable,
|
||||
fhc_intr_disable,
|
||||
fhc_intr_eoi
|
||||
};
|
||||
|
||||
struct fhc_icarg {
|
||||
struct fhc_softc *fica_sc;
|
||||
struct resource *fica_memres;
|
||||
};
|
||||
|
||||
static int
|
||||
fhc_probe(device_t dev)
|
||||
{
|
||||
@ -150,8 +147,9 @@ fhc_attach(device_t dev)
|
||||
{
|
||||
char ledname[sizeof("boardXX")];
|
||||
struct fhc_devinfo *fdi;
|
||||
struct sbus_regs *reg;
|
||||
struct fhc_icarg *fica;
|
||||
struct fhc_softc *sc;
|
||||
struct sbus_regs *reg;
|
||||
phandle_t child;
|
||||
phandle_t node;
|
||||
device_t cdev;
|
||||
@ -160,6 +158,7 @@ fhc_attach(device_t dev)
|
||||
uint32_t *intr;
|
||||
uint32_t iv;
|
||||
char *name;
|
||||
int central;
|
||||
int error;
|
||||
int i;
|
||||
int nintr;
|
||||
@ -169,8 +168,9 @@ fhc_attach(device_t dev)
|
||||
sc = device_get_softc(dev);
|
||||
node = ofw_bus_get_node(dev);
|
||||
|
||||
central = 0;
|
||||
if (strcmp(device_get_name(device_get_parent(dev)), "central") == 0)
|
||||
sc->sc_flags |= FHC_CENTRAL;
|
||||
central = 1;
|
||||
|
||||
for (i = 0; i < FHC_NREG; i++) {
|
||||
rid = i;
|
||||
@ -181,24 +181,20 @@ fhc_attach(device_t dev)
|
||||
error = ENXIO;
|
||||
goto fail_memres;
|
||||
}
|
||||
sc->sc_bt[i] = rman_get_bustag(sc->sc_memres[i]);
|
||||
sc->sc_bh[i] = rman_get_bushandle(sc->sc_memres[i]);
|
||||
}
|
||||
|
||||
if ((sc->sc_flags & FHC_CENTRAL) != 0) {
|
||||
board = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
|
||||
sc->sc_bh[FHC_INTERNAL], FHC_BSR);
|
||||
sc->sc_board = ((board >> 16) & 0x1) | ((board >> 12) & 0xe);
|
||||
if (central != 0) {
|
||||
board = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_BSR);
|
||||
board = ((board >> 16) & 0x1) | ((board >> 12) & 0xe);
|
||||
} else {
|
||||
if (OF_getprop(node, "board#", &sc->sc_board,
|
||||
sizeof(sc->sc_board)) == -1) {
|
||||
if (OF_getprop(node, "board#", &board, sizeof(board)) == -1) {
|
||||
device_printf(dev, "cannot get board number\n");
|
||||
error = ENXIO;
|
||||
goto fail_memres;
|
||||
}
|
||||
}
|
||||
|
||||
device_printf(dev, "board %d, ", sc->sc_board);
|
||||
device_printf(dev, "board %d, ", board);
|
||||
if (OF_getprop_alloc(node, "board-model", 1, (void **)&name) != -1) {
|
||||
printf("model %s\n", name);
|
||||
free(name, M_OFWPROP);
|
||||
@ -206,25 +202,20 @@ fhc_attach(device_t dev)
|
||||
printf("model unknown\n");
|
||||
|
||||
for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
|
||||
bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0);
|
||||
bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR);
|
||||
bus_write_4(sc->sc_memres[i], FHC_ICLR, 0x0);
|
||||
(void)bus_read_4(sc->sc_memres[i], FHC_ICLR);
|
||||
}
|
||||
|
||||
sc->sc_ign = sc->sc_board << 1;
|
||||
bus_space_write_4(sc->sc_bt[FHC_IGN], sc->sc_bh[FHC_IGN], 0x0,
|
||||
sc->sc_ign);
|
||||
sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN],
|
||||
sc->sc_bh[FHC_IGN], 0x0);
|
||||
sc->sc_ign = board << 1;
|
||||
bus_write_4(sc->sc_memres[FHC_IGN], 0x0, sc->sc_ign);
|
||||
sc->sc_ign = bus_read_4(sc->sc_memres[FHC_IGN], 0x0);
|
||||
|
||||
ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
|
||||
sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
|
||||
if ((sc->sc_flags & FHC_CENTRAL) == 0)
|
||||
ctrl = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
|
||||
if (central == 0)
|
||||
ctrl |= FHC_CTRL_IXIST;
|
||||
ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
|
||||
bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
|
||||
FHC_CTRL, ctrl);
|
||||
bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
|
||||
FHC_CTRL);
|
||||
bus_write_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL, ctrl);
|
||||
(void)bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
|
||||
|
||||
sc->sc_nrange = OF_getprop_alloc(node, "ranges",
|
||||
sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges);
|
||||
@ -234,8 +225,44 @@ fhc_attach(device_t dev)
|
||||
goto fail_memres;
|
||||
}
|
||||
|
||||
if ((sc->sc_flags & FHC_CENTRAL) == 0) {
|
||||
snprintf(ledname, sizeof(ledname), "board%d", sc->sc_board);
|
||||
/*
|
||||
* Apparently only the interrupt controller of boards hanging off
|
||||
* of central(4) is indented to be used, otherwise we would have
|
||||
* conflicts registering the interrupt controllers for all FHC
|
||||
* boards as the board number and thus the IGN isn't unique.
|
||||
*/
|
||||
if (central == 1) {
|
||||
/*
|
||||
* Hunt through all the interrupt mapping regs and register
|
||||
* our interrupt controller for the corresponding interrupt
|
||||
* vectors.
|
||||
*/
|
||||
for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
|
||||
fica = malloc(sizeof(*fica), M_DEVBUF, M_NOWAIT);
|
||||
if (fica == NULL)
|
||||
panic("%s: could not allocate interrupt "
|
||||
"controller argument", __func__);
|
||||
fica->fica_sc = sc;
|
||||
fica->fica_memres = sc->sc_memres[i];
|
||||
#ifdef FHC_DEBUG
|
||||
device_printf(dev, "intr map %d: %#lx, clr: %#lx\n", i,
|
||||
(u_long)bus_read_4(fica->fica_memres, FHC_IMAP),
|
||||
(u_long)bus_read_4(fica->fica_memres, FHC_ICLR));
|
||||
#endif
|
||||
/*
|
||||
* XXX we only pick the INO rather than the INR
|
||||
* from the IMR since the firmware may not provide
|
||||
* the IGN and the IGN is constant for all devices
|
||||
* on that FireHose controller.
|
||||
*/
|
||||
if (intr_controller_register(INTMAP_VEC(sc->sc_ign,
|
||||
INTINO(bus_read_4(fica->fica_memres, FHC_IMAP))),
|
||||
&fhc_ic, fica) != 0)
|
||||
panic("%s: could not register interrupt "
|
||||
"controller for map %d", __func__, i);
|
||||
}
|
||||
} else {
|
||||
snprintf(ledname, sizeof(ledname), "board%d", board);
|
||||
sc->sc_led_dev = led_create(fhc_led_func, sc, ledname);
|
||||
}
|
||||
|
||||
@ -260,16 +287,17 @@ fhc_attach(device_t dev)
|
||||
reg[i].sbr_offset, reg[i].sbr_offset +
|
||||
reg[i].sbr_size, reg[i].sbr_size);
|
||||
free(reg, M_OFWPROP);
|
||||
nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr),
|
||||
(void **)&intr);
|
||||
if (nintr != -1) {
|
||||
for (i = 0; i < nintr; i++) {
|
||||
iv = INTINO(intr[i]) |
|
||||
(sc->sc_ign << INTMAP_IGN_SHIFT);
|
||||
resource_list_add(&fdi->fdi_rl, SYS_RES_IRQ, i,
|
||||
iv, iv, 1);
|
||||
if (central == 1) {
|
||||
nintr = OF_getprop_alloc(child, "interrupts",
|
||||
sizeof(*intr), (void **)&intr);
|
||||
if (nintr != -1) {
|
||||
for (i = 0; i < nintr; i++) {
|
||||
iv = INTMAP_VEC(sc->sc_ign, intr[i]);
|
||||
resource_list_add(&fdi->fdi_rl,
|
||||
SYS_RES_IRQ, i, iv, iv, 1);
|
||||
}
|
||||
free(intr, M_OFWPROP);
|
||||
}
|
||||
free(intr, M_OFWPROP);
|
||||
}
|
||||
cdev = device_add_child(dev, NULL, -1);
|
||||
if (cdev == NULL) {
|
||||
@ -316,108 +344,56 @@ fhc_probe_nomatch(device_t dev, device_t child)
|
||||
type != NULL ? type : "unknown");
|
||||
}
|
||||
|
||||
static void
|
||||
fhc_intr_enable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct fhc_icarg *fica = iv->iv_icarg;
|
||||
|
||||
bus_write_4(fica->fica_memres, FHC_IMAP,
|
||||
INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
|
||||
(void)bus_read_4(fica->fica_memres, FHC_IMAP);
|
||||
}
|
||||
|
||||
static void
|
||||
fhc_intr_disable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct fhc_icarg *fica = iv->iv_icarg;
|
||||
|
||||
bus_write_4(fica->fica_memres, FHC_IMAP, iv->iv_vec);
|
||||
(void)bus_read_4(fica->fica_memres, FHC_IMAP);
|
||||
}
|
||||
|
||||
static void
|
||||
fhc_intr_eoi(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct fhc_icarg *fica = iv->iv_icarg;
|
||||
|
||||
bus_write_4(fica->fica_memres, FHC_ICLR, 0x0);
|
||||
(void)bus_read_4(fica->fica_memres, FHC_ICLR);
|
||||
}
|
||||
|
||||
static int
|
||||
fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
|
||||
driver_filter_t *filt, driver_intr_t *func, void *arg, void **cookiep)
|
||||
{
|
||||
struct fhc_softc *sc;
|
||||
struct fhc_clr *fc;
|
||||
bus_space_tag_t bt;
|
||||
bus_space_handle_t bh;
|
||||
int error;
|
||||
int i;
|
||||
long vec;
|
||||
uint32_t inr;
|
||||
|
||||
if (filt != NULL && func != NULL)
|
||||
return (EINVAL);
|
||||
u_long vec;
|
||||
|
||||
sc = device_get_softc(bus);
|
||||
/*
|
||||
* Make sure the vector is fully specified and we registered
|
||||
* our interrupt controller for it.
|
||||
*/
|
||||
vec = rman_get_start(r);
|
||||
|
||||
bt = NULL;
|
||||
bh = 0;
|
||||
inr = 0;
|
||||
for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
|
||||
if (INTINO(bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i],
|
||||
FHC_IMAP)) == INTINO(vec)){
|
||||
bt = sc->sc_bt[i];
|
||||
bh = sc->sc_bh[i];
|
||||
inr = INTINO(vec) | (sc->sc_ign << INTMAP_IGN_SHIFT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inr == 0)
|
||||
return (0);
|
||||
|
||||
fc = malloc(sizeof(*fc), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (fc == NULL)
|
||||
return (0);
|
||||
fc->fc_filter = filt;
|
||||
fc->fc_func = func;
|
||||
fc->fc_arg = arg;
|
||||
fc->fc_bt = bt;
|
||||
fc->fc_bh = bh;
|
||||
|
||||
bus_space_write_4(bt, bh, FHC_IMAP, inr);
|
||||
bus_space_read_4(bt, bh, FHC_IMAP);
|
||||
|
||||
error = bus_generic_setup_intr(bus, child, r, flags, fhc_filter_stub,
|
||||
fhc_intr_stub, fc, cookiep);
|
||||
if (error != 0) {
|
||||
free(fc, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
fc->fc_cookie = *cookiep;
|
||||
*cookiep = fc;
|
||||
|
||||
bus_space_write_4(bt, bh, FHC_ICLR, 0x0);
|
||||
bus_space_write_4(bt, bh, FHC_IMAP, INTMAP_ENABLE(inr, PCPU_GET(mid)));
|
||||
bus_space_read_4(bt, bh, FHC_IMAP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
fhc_teardown_intr(device_t bus, device_t child, struct resource *r,
|
||||
void *cookie)
|
||||
{
|
||||
struct fhc_clr *fc;
|
||||
int error;
|
||||
|
||||
fc = cookie;
|
||||
error = bus_generic_teardown_intr(bus, child, r, fc->fc_cookie);
|
||||
if (error != 0)
|
||||
free(fc, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
fhc_filter_stub(void *arg)
|
||||
{
|
||||
struct fhc_clr *fc = arg;
|
||||
int res;
|
||||
|
||||
if (fc->fc_filter != NULL) {
|
||||
res = fc->fc_filter(fc->fc_arg);
|
||||
bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0);
|
||||
bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR);
|
||||
} else
|
||||
res = FILTER_SCHEDULE_THREAD;
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
static void
|
||||
fhc_intr_stub(void *arg)
|
||||
{
|
||||
struct fhc_clr *fc = arg;
|
||||
|
||||
fc->fc_func(fc->fc_arg);
|
||||
if (fc->fc_filter == NULL) {
|
||||
bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0);
|
||||
bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR);
|
||||
}
|
||||
if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &fhc_ic) {
|
||||
device_printf(bus, "invalid interrupt vector 0x%lx\n", vec);
|
||||
return (EINVAL);
|
||||
}
|
||||
return (bus_generic_setup_intr(bus, child, r, flags, filt, func,
|
||||
arg, cookiep));
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
@ -441,13 +417,13 @@ fhc_alloc_resource(device_t bus, device_t child, int type, int *rid,
|
||||
rle = NULL;
|
||||
rl = BUS_GET_RESOURCE_LIST(bus, child);
|
||||
sc = device_get_softc(bus);
|
||||
rle = resource_list_find(rl, type, *rid);
|
||||
switch (type) {
|
||||
case SYS_RES_IRQ:
|
||||
return (resource_list_alloc(rl, bus, child, type, rid, start,
|
||||
end, count, flags));
|
||||
case SYS_RES_MEMORY:
|
||||
if (!passthrough) {
|
||||
rle = resource_list_find(rl, type, *rid);
|
||||
if (rle == NULL)
|
||||
return (NULL);
|
||||
if (rle->res != NULL)
|
||||
@ -505,17 +481,14 @@ fhc_led_func(void *arg, int onoff)
|
||||
|
||||
sc = (struct fhc_softc *)arg;
|
||||
|
||||
ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
|
||||
sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
|
||||
ctrl = bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
|
||||
if (onoff)
|
||||
ctrl |= FHC_CTRL_RLED;
|
||||
else
|
||||
ctrl &= ~FHC_CTRL_RLED;
|
||||
ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
|
||||
bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
|
||||
FHC_CTRL, ctrl);
|
||||
bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
|
||||
FHC_CTRL);
|
||||
bus_write_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL, ctrl);
|
||||
(void)bus_read_4(sc->sc_memres[FHC_INTERNAL], FHC_CTRL);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -51,12 +51,15 @@
|
||||
#define INTMAP_PCISLOT_MASK 0x00000000cLL /* PCI slot # */
|
||||
#define INTMAP_PCIINT_MASK 0x000000003LL /* PCI interrupt #A,#B,#C,#D */
|
||||
#define INTMAP_OBIO_MASK 0x000000020LL /* Onboard device */
|
||||
#define INTIGN(x) (((x) & INTMAP_IGN_MASK) >> INTMAP_IGN_SHIFT)
|
||||
#define INTVEC(x) ((x) & INTMAP_INR_MASK)
|
||||
#define INTSLOT(x) (((x) >> 3) & 0x7)
|
||||
#define INTPRI(x) ((x) & 0x7)
|
||||
#define INTINO(x) ((x) & INTMAP_INO_MASK)
|
||||
#define INTMAP_ENABLE(mr, mid) \
|
||||
(((mr) & ~INTMAP_TID_MASK) | ((mid) << INTMAP_TID_SHIFT) | INTMAP_V)
|
||||
#define INTMAP_VEC(ign, inr) \
|
||||
(((ign) << INTMAP_IGN_SHIFT) | (inr))
|
||||
|
||||
/* counter-timer support. */
|
||||
void sparc64_counter_init(bus_space_tag_t tag, bus_space_handle_t handle,
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
#define IH_SHIFT PTR_SHIFT
|
||||
#define IQE_SHIFT 5
|
||||
#define IV_SHIFT 5
|
||||
#define IV_SHIFT 6
|
||||
|
||||
#define PIL_LOW 1 /* stray interrupts */
|
||||
#define PIL_ITHREAD 2 /* interrupts that use ithreads */
|
||||
@ -56,8 +56,6 @@ struct trapframe;
|
||||
typedef void ih_func_t(struct trapframe *);
|
||||
typedef void iv_func_t(void *);
|
||||
|
||||
struct ithd;
|
||||
|
||||
struct intr_request {
|
||||
struct intr_request *ir_next;
|
||||
iv_func_t *ir_func;
|
||||
@ -66,12 +64,23 @@ struct intr_request {
|
||||
u_int ir_pri;
|
||||
};
|
||||
|
||||
struct intr_controller {
|
||||
void (*ic_enable)(void *);
|
||||
void (*ic_disable)(void *);
|
||||
void (*ic_eoi)(void *);
|
||||
};
|
||||
|
||||
struct intr_vector {
|
||||
iv_func_t *iv_func;
|
||||
void *iv_arg;
|
||||
const struct intr_controller *iv_ic;
|
||||
void *iv_icarg;
|
||||
struct intr_event *iv_event;
|
||||
u_int iv_pri;
|
||||
u_int iv_vec;
|
||||
u_int iv_mid;
|
||||
u_int iv_refcnt;
|
||||
u_int iv_pad[2];
|
||||
};
|
||||
|
||||
extern ih_func_t *intr_handlers[];
|
||||
@ -81,8 +90,10 @@ void intr_setup(int level, ih_func_t *ihf, int pri, iv_func_t *ivf,
|
||||
void *iva);
|
||||
void intr_init1(void);
|
||||
void intr_init2(void);
|
||||
int intr_controller_register(int vec, const struct intr_controller *ic,
|
||||
void *icarg);
|
||||
int inthand_add(const char *name, int vec, int (*filt)(void *),
|
||||
void (*handler)(void *), void *arg, int flags, void **cookiep);
|
||||
void (*handler)(void *), void *arg, int flags, void **cookiep);
|
||||
int inthand_remove(int vec, void *cookie);
|
||||
|
||||
ih_func_t intr_fast;
|
||||
|
@ -79,12 +79,14 @@ __FBSDID("$FreeBSD$");
|
||||
static const struct psycho_desc *psycho_find_desc(const struct psycho_desc *,
|
||||
const char *);
|
||||
static const struct psycho_desc *psycho_get_desc(device_t);
|
||||
static void psycho_set_intr(struct psycho_softc *, int, bus_addr_t,
|
||||
static void psycho_set_intr(struct psycho_softc *, u_int, bus_addr_t,
|
||||
driver_filter_t, driver_intr_t);
|
||||
static int psycho_find_intrmap(struct psycho_softc *, int, bus_addr_t *,
|
||||
static int psycho_find_intrmap(struct psycho_softc *, u_int, bus_addr_t *,
|
||||
bus_addr_t *, u_long *);
|
||||
static driver_filter_t psycho_filter_stub;
|
||||
static driver_intr_t psycho_intr_stub;
|
||||
static driver_filter_t psycho_dmasync;
|
||||
static void psycho_intr_enable(void *);
|
||||
static void psycho_intr_disable(void *);
|
||||
static void psycho_intr_eoi(void *);
|
||||
static bus_space_tag_t psycho_alloc_bus_tag(struct psycho_softc *, int);
|
||||
|
||||
/* Interrupt handlers */
|
||||
@ -173,17 +175,27 @@ static SLIST_HEAD(, psycho_softc) psycho_softcs =
|
||||
|
||||
static uint8_t psycho_pci_bus_cnt;
|
||||
|
||||
struct psycho_clr {
|
||||
struct psycho_softc *pci_sc;
|
||||
bus_addr_t pci_clr; /* clear register */
|
||||
driver_filter_t *pci_filter; /* filter to call */
|
||||
driver_intr_t *pci_handler; /* handler to call */
|
||||
void *pci_arg; /* argument for the handler */
|
||||
void *pci_cookie; /* parent bus int. cookie */
|
||||
device_t pci_ppb; /* farest PCI-PCI bridge */
|
||||
uint8_t pci_bus; /* bus of farest PCI device */
|
||||
uint8_t pci_slot; /* slot of farest PCI device */
|
||||
uint8_t pci_func; /* func. of farest PCI device */
|
||||
static const struct intr_controller psycho_ic = {
|
||||
psycho_intr_enable,
|
||||
psycho_intr_disable,
|
||||
psycho_intr_eoi
|
||||
};
|
||||
|
||||
struct psycho_icarg {
|
||||
struct psycho_softc *pica_sc;
|
||||
bus_addr_t pica_map;
|
||||
bus_addr_t pica_clr;
|
||||
};
|
||||
|
||||
struct psycho_dmasync {
|
||||
struct psycho_softc *pds_sc;
|
||||
driver_filter_t *pds_handler; /* handler to call */
|
||||
void *pds_arg; /* argument for the handler */
|
||||
void *pds_cookie; /* parent bus int. cookie */
|
||||
device_t pds_ppb; /* farest PCI-PCI bridge */
|
||||
uint8_t pds_bus; /* bus of farest PCI device */
|
||||
uint8_t pds_slot; /* slot of farest PCI device */
|
||||
uint8_t pds_func; /* func. of farest PCI device */
|
||||
};
|
||||
|
||||
#define PSYCHO_READ8(sc, off) \
|
||||
@ -288,19 +300,17 @@ static int
|
||||
psycho_attach(device_t dev)
|
||||
{
|
||||
char name[sizeof("pci108e,1000")];
|
||||
struct psycho_icarg *pica;
|
||||
struct psycho_softc *asc, *sc, *osc;
|
||||
struct ofw_pci_ranges *range;
|
||||
const struct psycho_desc *desc;
|
||||
phandle_t child, node;
|
||||
bus_addr_t intrclr, intrmap;
|
||||
uint64_t csr, dr;
|
||||
uint32_t dvmabase, psycho_br[2];
|
||||
int32_t rev;
|
||||
u_int ver;
|
||||
int i, n, nrange, rid;
|
||||
#ifdef PSYCHO_DEBUG
|
||||
bus_addr_t map, clr;
|
||||
uint64_t mr;
|
||||
#endif
|
||||
|
||||
node = ofw_bus_get_node(dev);
|
||||
sc = device_get_softc(dev);
|
||||
@ -375,11 +385,11 @@ psycho_attach(device_t dev)
|
||||
|
||||
csr = PSYCHO_READ8(sc, PSR_CS);
|
||||
ver = PSYCHO_GCSR_VERS(csr);
|
||||
sc->sc_ign = 0x7c0; /* Hummingbird/Sabre IGN is always 0x1f. */
|
||||
sc->sc_ign = 0x1f; /* Hummingbird/Sabre IGN is always 0x1f. */
|
||||
if (sc->sc_mode == PSYCHO_MODE_PSYCHO)
|
||||
sc->sc_ign = PSYCHO_GCSR_IGN(csr) << INTMAP_IGN_SHIFT;
|
||||
sc->sc_ign = PSYCHO_GCSR_IGN(csr);
|
||||
|
||||
device_printf(dev, "%s, impl %d, version %d, ign %#x, bus %c\n",
|
||||
device_printf(dev, "%s, impl %d, version %d, IGN %#x, bus %c\n",
|
||||
desc->pd_name, (u_int)PSYCHO_GCSR_IMPL(csr), ver, sc->sc_ign,
|
||||
'A' + sc->sc_half);
|
||||
|
||||
@ -488,18 +498,49 @@ psycho_attach(device_t dev)
|
||||
SLIST_INSERT_HEAD(&psycho_softcs, sc, sc_link);
|
||||
|
||||
/*
|
||||
* Register a PCI bus error interrupt handler according to which
|
||||
* half this is. Hummingbird/Sabre don't have a PCI bus B error
|
||||
* interrupt but they are also only used for PCI bus A.
|
||||
*/
|
||||
psycho_set_intr(sc, 0, sc->sc_half == 0 ? PSR_PCIAERR_INT_MAP :
|
||||
PSR_PCIBERR_INT_MAP, psycho_pci_bus, NULL);
|
||||
|
||||
/*
|
||||
* If we're a Hummingbird/Sabre or the first of a pair of Psycho's to
|
||||
* arrive here, start up the IOMMU.
|
||||
* If we're a Hummingbird/Sabre or the first of a pair of Psychos
|
||||
* to arrive here, do the interrupt setup and start up the IOMMU.
|
||||
*/
|
||||
if (osc == NULL) {
|
||||
/*
|
||||
* Hunt through all the interrupt mapping regs and register
|
||||
* our interrupt controller for the corresponding interrupt
|
||||
* vectors.
|
||||
*/
|
||||
for (n = 0; n <= PSYCHO_MAX_INO; n++) {
|
||||
if (psycho_find_intrmap(sc, n, &intrmap, &intrclr,
|
||||
NULL) == 0)
|
||||
continue;
|
||||
pica = malloc(sizeof(*pica), M_DEVBUF, M_NOWAIT);
|
||||
if (pica == NULL)
|
||||
panic("%s: could not allocate interrupt "
|
||||
"controller argument", __func__);
|
||||
pica->pica_sc = sc;
|
||||
pica->pica_map = intrmap;
|
||||
pica->pica_clr = intrclr;
|
||||
#ifdef PSYCHO_DEBUG
|
||||
/*
|
||||
* Enable all interrupts and clear all interrupt
|
||||
* states. This aids the debugging of interrupt
|
||||
* routing problems.
|
||||
*/
|
||||
device_printf(dev,
|
||||
"intr map (INO %d, %s) %#lx: %#lx, clr: %#lx\n",
|
||||
n, intrmap <= PSR_PCIB3_INT_MAP ? "PCI" : "OBIO",
|
||||
(u_long)intrmap, (u_long)PSYCHO_READ8(sc, intrmap),
|
||||
(u_long)intrclr);
|
||||
PSYCHO_WRITE8(sc, intrmap, INTMAP_VEC(sc->sc_ign, n));
|
||||
PSYCHO_WRITE8(sc, intrclr, 0);
|
||||
PSYCHO_WRITE8(sc, intrmap,
|
||||
INTMAP_ENABLE(INTMAP_VEC(sc->sc_ign, n),
|
||||
PCPU_GET(mid)));
|
||||
#endif
|
||||
if (intr_controller_register(INTMAP_VEC(sc->sc_ign, n),
|
||||
&psycho_ic, pica) != 0)
|
||||
panic("%s: could not register interrupt "
|
||||
"controller for INO %d", __func__, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Establish handlers for interesting interrupts...
|
||||
*
|
||||
@ -578,6 +619,14 @@ psycho_attach(device_t dev)
|
||||
iommu_reset(sc->sc_is);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a PCI bus error interrupt handler according to which
|
||||
* half this is. Hummingbird/Sabre don't have a PCI bus B error
|
||||
* interrupt but they are also only used for PCI bus A.
|
||||
*/
|
||||
psycho_set_intr(sc, 0, sc->sc_half == 0 ? PSR_PCIAERR_INT_MAP :
|
||||
PSR_PCIBERR_INT_MAP, psycho_pci_bus, NULL);
|
||||
|
||||
/* Allocate our tags. */
|
||||
sc->sc_pci_memt = psycho_alloc_bus_tag(sc, PCI_MEMORY_BUS_SPACE);
|
||||
sc->sc_pci_iot = psycho_alloc_bus_tag(sc, PCI_IO_BUS_SPACE);
|
||||
@ -590,31 +639,6 @@ psycho_attach(device_t dev)
|
||||
sc->sc_pci_dmat->dt_cookie = sc->sc_is;
|
||||
sc->sc_pci_dmat->dt_mt = &iommu_dma_methods;
|
||||
|
||||
#ifdef PSYCHO_DEBUG
|
||||
/*
|
||||
* Enable all interrupts and clear all interrupt states.
|
||||
* This aids the debugging of interrupt routing problems.
|
||||
*/
|
||||
for (map = PSR_PCIA0_INT_MAP, clr = PSR_PCIA0_INT_CLR, n = 0;
|
||||
map <= PSR_PCIB3_INT_MAP; map += 8, clr += 32, n++) {
|
||||
mr = PSYCHO_READ8(sc, map);
|
||||
device_printf(dev, "intr map (pci) %d: %#lx\n", n, (u_long)mr);
|
||||
PSYCHO_WRITE8(sc, map, mr & ~INTMAP_V);
|
||||
for (i = 0; i < 4; i++)
|
||||
PCICTL_WRITE8(sc, clr + i * 8, 0);
|
||||
PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
}
|
||||
for (map = PSR_SCSI_INT_MAP, clr = PSR_SCSI_INT_CLR, n = 0;
|
||||
map <= PSR_SERIAL_INT_MAP; map += 8, clr += 8, n++) {
|
||||
mr = PSYCHO_READ8(sc, map);
|
||||
device_printf(dev, "intr map (obio) %d: %#lx, clr: %#lx\n", n,
|
||||
(u_long)mr, (u_long)clr);
|
||||
PSYCHO_WRITE8(sc, map, mr & ~INTMAP_V);
|
||||
PSYCHO_WRITE8(sc, clr, 0);
|
||||
PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
}
|
||||
#endif /* PSYCHO_DEBUG */
|
||||
|
||||
/*
|
||||
* Get the bus range from the firmware; it is used solely for obtaining
|
||||
* the inital bus number, and cannot be trusted on all machines.
|
||||
@ -672,41 +696,59 @@ psycho_attach(device_t dev)
|
||||
}
|
||||
|
||||
static void
|
||||
psycho_set_intr(struct psycho_softc *sc, int index, bus_addr_t map,
|
||||
psycho_set_intr(struct psycho_softc *sc, u_int index, bus_addr_t intrmap,
|
||||
driver_filter_t filt, driver_intr_t intr)
|
||||
{
|
||||
uint64_t mr;
|
||||
u_long vec;
|
||||
int rid;
|
||||
|
||||
rid = index;
|
||||
mr = PSYCHO_READ8(sc, map);
|
||||
sc->sc_irq_res[index] = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ,
|
||||
&rid, RF_ACTIVE);
|
||||
if (sc->sc_irq_res[index] == NULL ||
|
||||
rman_get_start(sc->sc_irq_res[index]) != INTVEC(mr) ||
|
||||
INTIGN(vec = rman_get_start(sc->sc_irq_res[index])) != sc->sc_ign ||
|
||||
INTVEC(PSYCHO_READ8(sc, intrmap)) != vec ||
|
||||
intr_vectors[vec].iv_ic != &psycho_ic ||
|
||||
bus_setup_intr(sc->sc_dev, sc->sc_irq_res[index], INTR_TYPE_MISC,
|
||||
filt, intr, sc, &sc->sc_ihand[index]) != 0)
|
||||
panic("%s: failed to set up interrupt %d", __func__, index);
|
||||
PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
}
|
||||
|
||||
static int
|
||||
psycho_find_intrmap(struct psycho_softc *sc, int ino, bus_addr_t *intrmapptr,
|
||||
psycho_find_intrmap(struct psycho_softc *sc, u_int ino, bus_addr_t *intrmapptr,
|
||||
bus_addr_t *intrclrptr, bus_addr_t *intrdiagptr)
|
||||
{
|
||||
bus_addr_t intrmap, intrclr;
|
||||
uint64_t im;
|
||||
u_long diag;
|
||||
bus_addr_t intrclr, intrmap;
|
||||
uint64_t diag;
|
||||
int found;
|
||||
|
||||
/*
|
||||
* XXX we only compare INOs rather than INRs since the firmware may
|
||||
* not provide the IGN and the IGN is constant for all devices on
|
||||
* that PCI controller.
|
||||
* This could cause problems for the FFB/external interrupt which
|
||||
* has a full vector that can be set arbitrarily.
|
||||
*/
|
||||
|
||||
if (ino > PSYCHO_MAX_INO) {
|
||||
device_printf(sc->sc_dev, "out of range INO %d requested\n",
|
||||
ino);
|
||||
return (0);
|
||||
}
|
||||
|
||||
found = 0;
|
||||
/* Hunt thru OBIO first. */
|
||||
/* Hunt through OBIO first. */
|
||||
diag = PSYCHO_READ8(sc, PSR_OBIO_INT_DIAG);
|
||||
for (intrmap = PSR_SCSI_INT_MAP, intrclr = PSR_SCSI_INT_CLR;
|
||||
intrmap <= PSR_SERIAL_INT_MAP; intrmap += 8, intrclr += 8,
|
||||
intrmap <= PSR_PWRMGT_INT_MAP; intrmap += 8, intrclr += 8,
|
||||
diag >>= 2) {
|
||||
im = PSYCHO_READ8(sc, intrmap);
|
||||
if (INTINO(im) == ino) {
|
||||
if (sc->sc_mode == PSYCHO_MODE_SABRE &&
|
||||
(intrmap == PSR_TIMER0_INT_MAP ||
|
||||
intrmap == PSR_TIMER1_INT_MAP ||
|
||||
intrmap == PSR_PCIBERR_INT_MAP ||
|
||||
intrmap == PSR_PWRMGT_INT_MAP))
|
||||
continue;
|
||||
if (INTINO(PSYCHO_READ8(sc, intrmap)) == ino) {
|
||||
diag &= 2;
|
||||
found = 1;
|
||||
break;
|
||||
@ -721,10 +763,9 @@ psycho_find_intrmap(struct psycho_softc *sc, int ino, bus_addr_t *intrmapptr,
|
||||
diag >>= 8) {
|
||||
if (sc->sc_mode == PSYCHO_MODE_PSYCHO &&
|
||||
(intrmap == PSR_PCIA2_INT_MAP ||
|
||||
intrmap == PSR_PCIA3_INT_MAP))
|
||||
intrmap == PSR_PCIA3_INT_MAP))
|
||||
continue;
|
||||
im = PSYCHO_READ8(sc, intrmap);
|
||||
if (((im ^ ino) & 0x3c) == 0) {
|
||||
if (((PSYCHO_READ8(sc, intrmap) ^ ino) & 0x3c) == 0) {
|
||||
intrclr += 8 * (ino & 3);
|
||||
diag = (diag >> ((ino & 3) * 2)) & 2;
|
||||
found = 1;
|
||||
@ -778,7 +819,6 @@ psycho_ce(void *arg)
|
||||
"AFSR %#lx\n", (u_long)afar, (u_long)afsr);
|
||||
/* Clear the error bits that we caught. */
|
||||
PSYCHO_WRITE8(sc, PSR_CE_AFS, afsr & CEAFSR_ERRMASK);
|
||||
PSYCHO_WRITE8(sc, PSR_CE_INT_CLR, 0);
|
||||
mtx_unlock_spin(sc->sc_mtx);
|
||||
return (FILTER_HANDLED);
|
||||
}
|
||||
@ -804,8 +844,13 @@ psycho_powerfail(void *arg)
|
||||
struct psycho_softc *sc = arg;
|
||||
|
||||
kdb_enter("powerfail");
|
||||
PSYCHO_WRITE8(sc, PSR_POWER_INT_CLR, 0);
|
||||
#else
|
||||
static int shutdown;
|
||||
|
||||
/* As the interrupt is cleared we may be called multiple times. */
|
||||
if (shutdown != 0)
|
||||
return (FILTER_HANDLED);
|
||||
shutdown++;
|
||||
printf("Power Failure Detected: Shutting down NOW.\n");
|
||||
shutdown_nice(0);
|
||||
#endif
|
||||
@ -815,7 +860,12 @@ psycho_powerfail(void *arg)
|
||||
static void
|
||||
psycho_overtemp(void *arg)
|
||||
{
|
||||
static int shutdown;
|
||||
|
||||
/* As the interrupt is cleared we may be called multiple times. */
|
||||
if (shutdown != 0)
|
||||
return;
|
||||
shutdown++;
|
||||
printf("DANGER: OVER TEMPERATURE detected.\nShutting down NOW.\n");
|
||||
shutdown_nice(RB_POWEROFF);
|
||||
}
|
||||
@ -826,7 +876,6 @@ psycho_wakeup(void *arg)
|
||||
{
|
||||
struct psycho_softc *sc = arg;
|
||||
|
||||
PSYCHO_WRITE8(sc, PSR_PWRMGT_INT_CLR, 0);
|
||||
/* Gee, we don't really have a framework to deal with this properly. */
|
||||
device_printf(sc->sc_dev, "power management wakeup\n");
|
||||
return (FILTER_HANDLED);
|
||||
@ -989,32 +1038,42 @@ psycho_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
|
||||
}
|
||||
|
||||
static int
|
||||
psycho_filter_stub(void *arg)
|
||||
psycho_dmasync(void *arg)
|
||||
{
|
||||
struct psycho_clr *pc = arg;
|
||||
int res;
|
||||
struct psycho_dmasync *pds = arg;
|
||||
|
||||
if (pc->pci_filter != NULL) {
|
||||
res = pc->pci_filter(pc->pci_arg);
|
||||
PSYCHO_WRITE8(pc->pci_sc, pc->pci_clr, 0);
|
||||
} else
|
||||
res = FILTER_SCHEDULE_THREAD;
|
||||
return (res);
|
||||
(void)PCIB_READ_CONFIG(pds->pds_ppb, pds->pds_bus, pds->pds_slot,
|
||||
pds->pds_func, PCIR_VENDOR, 2);
|
||||
(void)PSYCHO_READ8(pds->pds_sc, PSR_DMA_WRITE_SYNC);
|
||||
return (pds->pds_handler(pds->pds_arg));
|
||||
}
|
||||
|
||||
static void
|
||||
psycho_intr_stub(void *arg)
|
||||
psycho_intr_enable(void *arg)
|
||||
{
|
||||
struct psycho_clr *pc = arg;
|
||||
struct intr_vector *iv = arg;
|
||||
struct psycho_icarg *pica = iv->iv_icarg;
|
||||
|
||||
if (pc->pci_ppb != NULL) {
|
||||
(void)PCIB_READ_CONFIG(pc->pci_ppb, pc->pci_bus, pc->pci_slot,
|
||||
pc->pci_func, PCIR_VENDOR, 2);
|
||||
(void)PSYCHO_READ8(pc->pci_sc, PSR_DMA_WRITE_SYNC);
|
||||
}
|
||||
pc->pci_handler(pc->pci_arg);
|
||||
if (pc->pci_filter == NULL)
|
||||
PSYCHO_WRITE8(pc->pci_sc, pc->pci_clr, 0);
|
||||
PSYCHO_WRITE8(pica->pica_sc, pica->pica_map,
|
||||
INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
|
||||
}
|
||||
|
||||
static void
|
||||
psycho_intr_disable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct psycho_icarg *pica = iv->iv_icarg;
|
||||
|
||||
PSYCHO_WRITE8(pica->pica_sc, pica->pica_map, iv->iv_vec);
|
||||
}
|
||||
|
||||
static void
|
||||
psycho_intr_eoi(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct psycho_icarg *pica = iv->iv_icarg;
|
||||
|
||||
PSYCHO_WRITE8(pica->pica_sc, pica->pica_clr, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1029,62 +1088,39 @@ psycho_setup_intr(device_t dev, device_t child, struct resource *ires,
|
||||
devclass_t pci_devclass;
|
||||
device_t cdev, pdev, pcidev;
|
||||
struct psycho_softc *sc;
|
||||
struct psycho_clr *pc;
|
||||
bus_addr_t intrmapptr, intrclrptr;
|
||||
long vec;
|
||||
uint64_t mr;
|
||||
int error, ino;
|
||||
|
||||
if (filt != NULL && intr != NULL)
|
||||
return (EINVAL);
|
||||
struct psycho_dmasync *pds;
|
||||
u_long vec;
|
||||
int error;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
pc = malloc(sizeof(*pc), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (pc == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
/*
|
||||
* Hunt through all the interrupt mapping regs to look for our
|
||||
* interrupt vector.
|
||||
*
|
||||
* XXX We only compare INOs rather than IGNs since the firmware may
|
||||
* not provide the IGN and the IGN is constant for all devices on that
|
||||
* PCI controller. This could cause problems for the FFB/external
|
||||
* interrupt which has a full vector that can be set arbitrarily.
|
||||
* Make sure the vector is fully specified and we registered
|
||||
* our interrupt controller for it.
|
||||
*/
|
||||
vec = rman_get_start(ires);
|
||||
ino = INTINO(vec);
|
||||
if (!psycho_find_intrmap(sc, ino, &intrmapptr, &intrclrptr, NULL)) {
|
||||
device_printf(dev, "cannot find interrupt vector 0x%lx\n", vec);
|
||||
free(pc, M_DEVBUF);
|
||||
if (INTIGN(vec) != sc->sc_ign ||
|
||||
intr_vectors[vec].iv_ic != &psycho_ic) {
|
||||
device_printf(dev, "invalid interrupt vector 0x%lx\n", vec);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
#ifdef PSYCHO_DEBUG
|
||||
device_printf(dev, "%s: INO %d, map %#lx, clr %#lx\n", __func__, ino,
|
||||
(u_long)intrmapptr, (u_long)intrclrptr);
|
||||
#endif
|
||||
|
||||
pc->pci_sc = sc;
|
||||
pc->pci_arg = arg;
|
||||
pc->pci_filter = filt;
|
||||
pc->pci_handler = intr;
|
||||
pc->pci_clr = intrclrptr;
|
||||
|
||||
/*
|
||||
* The Sabre-APB-combination has a bug where it does not drain
|
||||
* DMA write data for devices behind additional PCI-PCI bridges
|
||||
* underneath the APB PCI-PCI bridge. The workaround is to do
|
||||
* a read on the farest PCI-PCI bridge followed by a read of the
|
||||
* PCI DMA write sync register of the Sabre.
|
||||
* XXX installing the workaround for an affected device and the
|
||||
* actual workaround in psycho_intr_stub() should be moved to
|
||||
* XXX installing the wrapper for an affected device and the
|
||||
* actual workaround in psycho_dmasync() should be moved to
|
||||
* psycho(4)-specific bus_dma_tag_create() and bus_dmamap_sync()
|
||||
* methods, respectively, once DMA tag creation is newbus'ified,
|
||||
* so the workaround isn't only applied for interrupt handlers
|
||||
* but also for polling(4) callbacks.
|
||||
*/
|
||||
if (sc->sc_mode == PSYCHO_MODE_SABRE) {
|
||||
pds = malloc(sizeof(*pds), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (pds == NULL)
|
||||
return (ENOMEM);
|
||||
pcidev = NULL;
|
||||
found.apb = found.ppb = 0;
|
||||
pci_devclass = devclass_find("pci");
|
||||
@ -1109,59 +1145,63 @@ psycho_setup_intr(device_t dev, device_t child, struct resource *ires,
|
||||
found.ppb = 1;
|
||||
}
|
||||
if (found.apb && found.ppb && pcidev != NULL) {
|
||||
pc->pci_ppb =
|
||||
pds->pds_sc = sc;
|
||||
pds->pds_arg = arg;
|
||||
pds->pds_ppb =
|
||||
device_get_parent(device_get_parent(pcidev));
|
||||
pc->pci_bus = pci_get_bus(pcidev);
|
||||
pc->pci_slot = pci_get_slot(pcidev);
|
||||
pc->pci_func = pci_get_function(pcidev);
|
||||
pds->pds_bus = pci_get_bus(pcidev);
|
||||
pds->pds_slot = pci_get_slot(pcidev);
|
||||
pds->pds_func = pci_get_function(pcidev);
|
||||
if (bootverbose)
|
||||
device_printf(dev, "installed DMA sync "
|
||||
"workaround for device %d.%d on bus %d\n",
|
||||
pc->pci_slot, pc->pci_func, pc->pci_bus);
|
||||
pds->pds_slot, pds->pds_func,
|
||||
pds->pds_bus);
|
||||
if (intr == NULL) {
|
||||
pds->pds_handler = filt;
|
||||
error = bus_generic_setup_intr(dev, child,
|
||||
ires, flags, psycho_dmasync, intr, pds,
|
||||
cookiep);
|
||||
} else {
|
||||
pds->pds_handler = (driver_filter_t *)intr;
|
||||
error = bus_generic_setup_intr(dev, child,
|
||||
ires, flags, filt,
|
||||
(driver_intr_t *)psycho_dmasync, pds,
|
||||
cookiep);
|
||||
}
|
||||
} else
|
||||
error = bus_generic_setup_intr(dev, child, ires,
|
||||
flags, filt, intr, arg, cookiep);
|
||||
if (error != 0) {
|
||||
free(pds, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the interrupt while we fiddle with it. */
|
||||
mr = PSYCHO_READ8(sc, intrmapptr);
|
||||
PSYCHO_WRITE8(sc, intrmapptr, mr & ~INTMAP_V);
|
||||
error = BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
|
||||
psycho_filter_stub, psycho_intr_stub, pc, cookiep);
|
||||
if (error != 0) {
|
||||
free(pc, M_DEVBUF);
|
||||
pds->pds_cookie = *cookiep;
|
||||
*cookiep = pds;
|
||||
return (error);
|
||||
}
|
||||
pc->pci_cookie = *cookiep;
|
||||
*cookiep = pc;
|
||||
|
||||
/*
|
||||
* Clear the interrupt, it might have been triggered before it was
|
||||
* set up.
|
||||
*/
|
||||
PSYCHO_WRITE8(sc, intrclrptr, 0);
|
||||
/*
|
||||
* Enable the interrupt and program the target module now we have the
|
||||
* handler installed.
|
||||
*/
|
||||
PSYCHO_WRITE8(sc, intrmapptr, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
return (error);
|
||||
return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr,
|
||||
arg, cookiep));
|
||||
}
|
||||
|
||||
static int
|
||||
psycho_teardown_intr(device_t dev, device_t child, struct resource *vec,
|
||||
void *cookie)
|
||||
{
|
||||
struct psycho_clr *pc = cookie;
|
||||
struct psycho_softc *sc;
|
||||
struct psycho_dmasync *pds;
|
||||
int error;
|
||||
|
||||
error = BUS_TEARDOWN_INTR(device_get_parent(dev), child, vec,
|
||||
pc->pci_cookie);
|
||||
/*
|
||||
* Don't disable the interrupt for now, so that stray interupts get
|
||||
* detected...
|
||||
*/
|
||||
if (error != 0)
|
||||
free(pc, M_DEVBUF);
|
||||
return (error);
|
||||
sc = device_get_softc(dev);
|
||||
if (sc->sc_mode == PSYCHO_MODE_SABRE) {
|
||||
pds = cookie;
|
||||
error = bus_generic_teardown_intr(dev, child, vec,
|
||||
pds->pds_cookie);
|
||||
if (error == 0)
|
||||
free(pds, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
return (bus_generic_teardown_intr(dev, child, vec, cookie));
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
@ -1188,7 +1228,7 @@ psycho_alloc_resource(device_t bus, device_t child, int type, int *rid,
|
||||
*/
|
||||
if (start != end)
|
||||
panic("%s: XXX: interrupt range", __func__);
|
||||
start = end |= sc->sc_ign;
|
||||
start = end = INTMAP_VEC(sc->sc_ign, end);
|
||||
return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type,
|
||||
rid, start, end, count, flags));
|
||||
}
|
||||
@ -1276,7 +1316,7 @@ psycho_release_resource(device_t bus, device_t child, int type, int rid,
|
||||
if (rman_get_flags(r) & RF_ACTIVE) {
|
||||
error = bus_deactivate_resource(child, type, rid, r);
|
||||
if (error)
|
||||
return error;
|
||||
return (error);
|
||||
}
|
||||
return (rman_release_resource(r));
|
||||
}
|
||||
@ -1297,7 +1337,7 @@ psycho_intr_pending(device_t dev, ofw_pci_intr_t intr)
|
||||
u_long diag;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (!psycho_find_intrmap(sc, intr, NULL, NULL, &diag)) {
|
||||
if (psycho_find_intrmap(sc, intr, NULL, NULL, &diag) == 0) {
|
||||
device_printf(dev, "%s: mapping not found for %d\n", __func__,
|
||||
intr);
|
||||
return (0);
|
||||
|
@ -205,6 +205,9 @@
|
||||
#define PCR_TAS 0x0028 /* PCI target address space reg (IIi) */
|
||||
#define PCR_STRBUF 0x0800 /* IOMMU streaming buffer registers. */
|
||||
|
||||
/* INO defines */
|
||||
#define PSYCHO_MAX_INO 0x3f
|
||||
|
||||
/* Device space defines */
|
||||
#define PSYCHO_CONF_SIZE 0x1000000
|
||||
#define PSYCHO_CONF_BUS_SHIFT 16
|
||||
|
@ -153,6 +153,7 @@ struct sbus_rd {
|
||||
};
|
||||
|
||||
struct sbus_softc {
|
||||
device_t sc_dev;
|
||||
bus_dma_tag_t sc_cdmatag;
|
||||
bus_space_tag_t sc_cbustag;
|
||||
int sc_clockfreq; /* clock frequency (in Hz) */
|
||||
@ -170,15 +171,6 @@ struct sbus_softc {
|
||||
void *sc_pf_ihand;
|
||||
};
|
||||
|
||||
struct sbus_clr {
|
||||
struct sbus_softc *scl_sc;
|
||||
bus_addr_t scl_clr; /* clear register */
|
||||
driver_filter_t *scl_filter; /* filter to call */
|
||||
driver_intr_t *scl_handler; /* handler to call */
|
||||
void *scl_arg; /* argument for the handler */
|
||||
void *scl_cookie; /* parent bus int. cookie */
|
||||
};
|
||||
|
||||
#define SYSIO_READ8(sc, off) \
|
||||
bus_read_8((sc)->sc_sysio_res, (off))
|
||||
#define SYSIO_WRITE8(sc, off, v) \
|
||||
@ -191,7 +183,6 @@ static bus_probe_nomatch_t sbus_probe_nomatch;
|
||||
static bus_read_ivar_t sbus_read_ivar;
|
||||
static bus_get_resource_list_t sbus_get_resource_list;
|
||||
static bus_setup_intr_t sbus_setup_intr;
|
||||
static bus_teardown_intr_t sbus_teardown_intr;
|
||||
static bus_alloc_resource_t sbus_alloc_resource;
|
||||
static bus_release_resource_t sbus_release_resource;
|
||||
static bus_activate_resource_t sbus_activate_resource;
|
||||
@ -203,8 +194,11 @@ static int sbus_inlist(const char *, const char **);
|
||||
static struct sbus_devinfo * sbus_setup_dinfo(device_t, struct sbus_softc *,
|
||||
phandle_t);
|
||||
static void sbus_destroy_dinfo(struct sbus_devinfo *);
|
||||
static driver_filter_t sbus_filter_stub;
|
||||
static driver_intr_t sbus_intr_stub;
|
||||
static void sbus_intr_enable(void *);
|
||||
static void sbus_intr_disable(void *);
|
||||
static void sbus_intr_eoi(void *);
|
||||
static int sbus_find_intrmap(struct sbus_softc *, u_int, bus_addr_t *,
|
||||
bus_addr_t *);
|
||||
static bus_space_tag_t sbus_alloc_bustag(struct sbus_softc *);
|
||||
static driver_intr_t sbus_overtemp;
|
||||
static driver_intr_t sbus_pwrfail;
|
||||
@ -223,7 +217,7 @@ static device_method_t sbus_methods[] = {
|
||||
DEVMETHOD(bus_probe_nomatch, sbus_probe_nomatch),
|
||||
DEVMETHOD(bus_read_ivar, sbus_read_ivar),
|
||||
DEVMETHOD(bus_setup_intr, sbus_setup_intr),
|
||||
DEVMETHOD(bus_teardown_intr, sbus_teardown_intr),
|
||||
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
|
||||
DEVMETHOD(bus_alloc_resource, sbus_alloc_resource),
|
||||
DEVMETHOD(bus_activate_resource, sbus_activate_resource),
|
||||
DEVMETHOD(bus_deactivate_resource, sbus_deactivate_resource),
|
||||
@ -256,6 +250,18 @@ DRIVER_MODULE(sbus, nexus, sbus_driver, sbus_devclass, 0, 0);
|
||||
#define OFW_SBUS_TYPE "sbus"
|
||||
#define OFW_SBUS_NAME "sbus"
|
||||
|
||||
static const struct intr_controller sbus_ic = {
|
||||
sbus_intr_enable,
|
||||
sbus_intr_disable,
|
||||
sbus_intr_eoi
|
||||
};
|
||||
|
||||
struct sbus_icarg {
|
||||
struct sbus_softc *sica_sc;
|
||||
bus_addr_t sica_map;
|
||||
bus_addr_t sica_clr;
|
||||
};
|
||||
|
||||
static const char *sbus_order_first[] = {
|
||||
"auxio",
|
||||
"dma",
|
||||
@ -294,18 +300,20 @@ sbus_attach(device_t dev)
|
||||
{
|
||||
struct sbus_softc *sc;
|
||||
struct sbus_devinfo *sdi;
|
||||
struct sbus_icarg *sica;
|
||||
struct sbus_ranges *range;
|
||||
struct resource *res;
|
||||
struct resource_list *rl;
|
||||
device_t cdev;
|
||||
bus_addr_t phys;
|
||||
bus_addr_t intrclr, intrmap, phys;
|
||||
bus_size_t size;
|
||||
char *name;
|
||||
u_long vec;
|
||||
phandle_t child, node;
|
||||
u_int64_t mr;
|
||||
int clock, i, intr, rid;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->sc_dev = dev;
|
||||
node = ofw_bus_get_node(dev);
|
||||
|
||||
rid = 0;
|
||||
@ -316,7 +324,7 @@ sbus_attach(device_t dev)
|
||||
|
||||
if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1)
|
||||
panic("%s: cannot get IGN", __func__);
|
||||
sc->sc_ign = (intr & INTMAP_IGN_MASK) >> INTMAP_IGN_SHIFT;
|
||||
sc->sc_ign = INTIGN(intr);
|
||||
sc->sc_cbustag = sbus_alloc_bustag(sc);
|
||||
|
||||
/*
|
||||
@ -413,27 +421,54 @@ sbus_attach(device_t dev)
|
||||
sc->sc_cdmatag->dt_cookie = &sc->sc_is;
|
||||
sc->sc_cdmatag->dt_mt = &iommu_dma_methods;
|
||||
|
||||
/*
|
||||
* Hunt through all the interrupt mapping regs and register our
|
||||
* interrupt controller for the corresponding interrupt vectors.
|
||||
*/
|
||||
for (i = 0; i <= SBUS_MAX_INO; i++) {
|
||||
if (sbus_find_intrmap(sc, i, &intrmap, &intrclr) == 0)
|
||||
continue;
|
||||
sica = malloc(sizeof(*sica), M_DEVBUF, M_NOWAIT);
|
||||
if (sica == NULL)
|
||||
panic("%s: could not allocate interrupt controller "
|
||||
"argument", __func__);
|
||||
sica->sica_sc = sc;
|
||||
sica->sica_map = intrmap;
|
||||
sica->sica_clr = intrclr;
|
||||
#ifdef SBUS_DEBUG
|
||||
device_printf(dev,
|
||||
"intr map (INO %d, %s) %#lx: %#lx, clr: %#lx\n",
|
||||
i, (i & INTMAP_OBIO_MASK) == 0 ? "SBus slot" : "OBIO",
|
||||
(u_long)intrmap, (u_long)SYSIO_READ8(sc, intrmap),
|
||||
(u_long)intrclr);
|
||||
#endif
|
||||
if (intr_controller_register(INTMAP_VEC(sc->sc_ign, i),
|
||||
&sbus_ic, sica) != 0)
|
||||
panic("%s: could not register interrupt controller "
|
||||
"for INO %d", __func__, i);
|
||||
}
|
||||
|
||||
/* Enable the over-temperature and power-fail interrupts. */
|
||||
rid = 4;
|
||||
mr = SYSIO_READ8(sc, SBR_THERM_INT_MAP);
|
||||
sc->sc_ot_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->sc_ot_ires == NULL ||
|
||||
rman_get_start(sc->sc_ot_ires) != INTVEC(mr) ||
|
||||
INTIGN(vec = rman_get_start(sc->sc_ot_ires)) != sc->sc_ign ||
|
||||
INTVEC(SYSIO_READ8(sc, SBR_THERM_INT_MAP)) != vec ||
|
||||
intr_vectors[vec].iv_ic != &sbus_ic ||
|
||||
bus_setup_intr(dev, sc->sc_ot_ires, INTR_TYPE_MISC,
|
||||
NULL, sbus_overtemp, sc, &sc->sc_ot_ihand) != 0)
|
||||
panic("%s: failed to set up temperature interrupt", __func__);
|
||||
SYSIO_WRITE8(sc, SBR_THERM_INT_MAP, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
rid = 3;
|
||||
mr = SYSIO_READ8(sc, SBR_POWER_INT_MAP);
|
||||
sc->sc_pf_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->sc_pf_ires == NULL ||
|
||||
rman_get_start(sc->sc_pf_ires) != INTVEC(mr) ||
|
||||
INTIGN(vec = rman_get_start(sc->sc_pf_ires)) != sc->sc_ign ||
|
||||
INTVEC(SYSIO_READ8(sc, SBR_POWER_INT_MAP)) != vec ||
|
||||
intr_vectors[vec].iv_ic != &sbus_ic ||
|
||||
bus_setup_intr(dev, sc->sc_pf_ires, INTR_TYPE_MISC,
|
||||
NULL, sbus_pwrfail, sc, &sc->sc_pf_ihand) != 0)
|
||||
panic("%s: failed to set up power fail interrupt", __func__);
|
||||
SYSIO_WRITE8(sc, SBR_POWER_INT_MAP, INTMAP_ENABLE(mr, PCPU_GET(mid)));
|
||||
|
||||
/* Initialize the counter-timer. */
|
||||
sparc64_counter_init(rman_get_bustag(sc->sc_sysio_res),
|
||||
@ -532,8 +567,7 @@ sbus_setup_dinfo(device_t dev, struct sbus_softc *sc, phandle_t node)
|
||||
*/
|
||||
if ((iv & INTMAP_OBIO_MASK) == 0)
|
||||
iv |= slot << 3;
|
||||
/* Set the ign as appropriate. */
|
||||
iv |= sc->sc_ign << INTMAP_IGN_SHIFT;
|
||||
iv = INTMAP_VEC(sc->sc_ign, iv);
|
||||
resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, i,
|
||||
iv, iv, 1);
|
||||
}
|
||||
@ -624,30 +658,68 @@ sbus_get_resource_list(device_t dev, device_t child)
|
||||
return (&sdi->sdi_rl);
|
||||
}
|
||||
|
||||
static int
|
||||
sbus_filter_stub(void *arg)
|
||||
static void
|
||||
sbus_intr_enable(void *arg)
|
||||
{
|
||||
struct sbus_clr *scl;
|
||||
int res;
|
||||
struct intr_vector *iv = arg;
|
||||
struct sbus_icarg *sica = iv->iv_icarg;
|
||||
|
||||
scl = (struct sbus_clr *)arg;
|
||||
if (scl->scl_filter != NULL) {
|
||||
res = scl->scl_filter(scl->scl_arg);
|
||||
SYSIO_WRITE8(scl->scl_sc, scl->scl_clr, 0);
|
||||
} else
|
||||
res = FILTER_SCHEDULE_THREAD;
|
||||
return (res);
|
||||
SYSIO_WRITE8(sica->sica_sc, sica->sica_map,
|
||||
INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
|
||||
}
|
||||
static void
|
||||
sbus_intr_disable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct sbus_icarg *sica = iv->iv_icarg;
|
||||
|
||||
SYSIO_WRITE8(sica->sica_sc, sica->sica_map, iv->iv_vec);
|
||||
}
|
||||
|
||||
static void
|
||||
sbus_intr_stub(void *arg)
|
||||
sbus_intr_eoi(void *arg)
|
||||
{
|
||||
struct sbus_clr *scl;
|
||||
struct intr_vector *iv = arg;
|
||||
struct sbus_icarg *sica = iv->iv_icarg;
|
||||
|
||||
scl = (struct sbus_clr *)arg;
|
||||
scl->scl_handler(scl->scl_arg);
|
||||
if (scl->scl_filter == NULL)
|
||||
SYSIO_WRITE8(scl->scl_sc, scl->scl_clr, 0);
|
||||
SYSIO_WRITE8(sica->sica_sc, sica->sica_clr, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
sbus_find_intrmap(struct sbus_softc *sc, u_int ino, bus_addr_t *intrmapptr,
|
||||
bus_addr_t *intrclrptr)
|
||||
{
|
||||
bus_addr_t intrclr, intrmap;
|
||||
int i;
|
||||
|
||||
if (ino > SBUS_MAX_INO) {
|
||||
device_printf(sc->sc_dev, "out of range INO %d requested\n",
|
||||
ino);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if ((ino & INTMAP_OBIO_MASK) == 0) {
|
||||
intrmap = SBR_SLOT0_INT_MAP + INTSLOT(ino) * 8;
|
||||
intrclr = SBR_SLOT0_INT_CLR +
|
||||
(INTSLOT(ino) * 8 * 8) + (INTPRI(ino) * 8);
|
||||
} else {
|
||||
intrclr = 0;
|
||||
for (i = 0, intrmap = SBR_SCSI_INT_MAP;
|
||||
intrmap <= SBR_RESERVED_INT_MAP; intrmap += 8, i++) {
|
||||
if (INTVEC(SYSIO_READ8(sc, intrmap)) ==
|
||||
INTMAP_VEC(sc->sc_ign, ino)) {
|
||||
intrclr = SBR_SCSI_INT_CLR + i * 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (intrclr == 0)
|
||||
return (0);
|
||||
}
|
||||
if (intrmapptr != NULL)
|
||||
*intrmapptr = intrmap;
|
||||
if (intrclrptr != NULL)
|
||||
*intrclrptr = intrclr;
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -655,97 +727,20 @@ sbus_setup_intr(device_t dev, device_t child, struct resource *ires, int flags,
|
||||
driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
|
||||
{
|
||||
struct sbus_softc *sc;
|
||||
struct sbus_clr *scl;
|
||||
bus_addr_t intrmapptr, intrclrptr, intrptr;
|
||||
u_int64_t intrmap;
|
||||
u_int32_t inr, slot;
|
||||
int error, i;
|
||||
long vec;
|
||||
u_long vec;
|
||||
|
||||
if (filt != NULL && intr != NULL)
|
||||
return (EINVAL);
|
||||
sc = device_get_softc(dev);
|
||||
scl = (struct sbus_clr *)malloc(sizeof(*scl), M_DEVBUF, M_NOWAIT);
|
||||
if (scl == NULL)
|
||||
return (ENOMEM);
|
||||
intrptr = intrmapptr = intrclrptr = 0;
|
||||
intrmap = 0;
|
||||
/*
|
||||
* Make sure the vector is fully specified and we registered
|
||||
* our interrupt controller for it.
|
||||
*/
|
||||
vec = rman_get_start(ires);
|
||||
inr = INTVEC(vec);
|
||||
if ((inr & INTMAP_OBIO_MASK) == 0) {
|
||||
/*
|
||||
* We're in an SBus slot, register the map and clear
|
||||
* intr registers.
|
||||
*/
|
||||
slot = INTSLOT(vec);
|
||||
intrmapptr = SBR_SLOT0_INT_MAP + slot * 8;
|
||||
intrclrptr = SBR_SLOT0_INT_CLR +
|
||||
(slot * 8 * 8) + (INTPRI(vec) * 8);
|
||||
/* Enable the interrupt, insert IGN. */
|
||||
intrmap = inr | (sc->sc_ign << INTMAP_IGN_SHIFT);
|
||||
} else {
|
||||
intrptr = SBR_SCSI_INT_MAP;
|
||||
/* Insert IGN */
|
||||
inr |= sc->sc_ign << INTMAP_IGN_SHIFT;
|
||||
for (i = 0; intrptr <= SBR_RESERVED_INT_MAP &&
|
||||
INTVEC(intrmap = SYSIO_READ8(sc, intrptr)) != inr;
|
||||
intrptr += 8, i++)
|
||||
;
|
||||
if (INTVEC(intrmap) == inr) {
|
||||
/* Register the map and clear intr registers */
|
||||
intrmapptr = intrptr;
|
||||
intrclrptr = SBR_SCSI_INT_CLR + i * 8;
|
||||
/* Enable the interrupt */
|
||||
} else
|
||||
panic("%s: IRQ not found!", __func__);
|
||||
}
|
||||
|
||||
scl->scl_sc = sc;
|
||||
scl->scl_arg = arg;
|
||||
scl->scl_filter = filt;
|
||||
scl->scl_handler = intr;
|
||||
scl->scl_clr = intrclrptr;
|
||||
/* Disable the interrupt while we fiddle with it */
|
||||
SYSIO_WRITE8(sc, intrmapptr, intrmap & ~INTMAP_V);
|
||||
error = BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
|
||||
sbus_filter_stub, sbus_intr_stub, scl, cookiep);
|
||||
if (error != 0) {
|
||||
free(scl, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
scl->scl_cookie = *cookiep;
|
||||
*cookiep = scl;
|
||||
|
||||
/*
|
||||
* Clear the interrupt, it might have been triggered before it was
|
||||
* set up.
|
||||
*/
|
||||
SYSIO_WRITE8(sc, intrclrptr, 0);
|
||||
/*
|
||||
* Enable the interrupt and program the target module now we have the
|
||||
* handler installed.
|
||||
*/
|
||||
SYSIO_WRITE8(sc, intrmapptr, INTMAP_ENABLE(intrmap, PCPU_GET(mid)));
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
sbus_teardown_intr(device_t dev, device_t child, struct resource *vec,
|
||||
void *cookie)
|
||||
{
|
||||
struct sbus_clr *scl;
|
||||
int error;
|
||||
|
||||
scl = (struct sbus_clr *)cookie;
|
||||
error = BUS_TEARDOWN_INTR(device_get_parent(dev), child, vec,
|
||||
scl->scl_cookie);
|
||||
/*
|
||||
* Don't disable the interrupt for now, so that stray interrupts get
|
||||
* detected...
|
||||
*/
|
||||
if (error != 0)
|
||||
free(scl, M_DEVBUF);
|
||||
return (error);
|
||||
if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &sbus_ic) {
|
||||
device_printf(dev, "invalid interrupt vector 0x%lx\n", vec);
|
||||
return (EINVAL);
|
||||
}
|
||||
return (bus_generic_setup_intr(dev, child, ires, flags, filt, intr,
|
||||
arg, cookiep));
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
@ -791,7 +786,7 @@ sbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
|
||||
bh = toffs = tend = 0;
|
||||
schild = child;
|
||||
while (device_get_parent(schild) != bus)
|
||||
schild = device_get_parent(child);
|
||||
schild = device_get_parent(schild);
|
||||
slot = sbus_get_slot(schild);
|
||||
for (i = 0; i < sc->sc_nrange; i++) {
|
||||
if (sc->sc_rd[i].rd_slot != slot ||
|
||||
@ -931,7 +926,12 @@ sbus_get_devinfo(device_t bus, device_t child)
|
||||
static void
|
||||
sbus_overtemp(void *arg)
|
||||
{
|
||||
static int shutdown;
|
||||
|
||||
/* As the interrupt is cleared we may be called multiple times. */
|
||||
if (shutdown != 0)
|
||||
return;
|
||||
shutdown++;
|
||||
printf("DANGER: OVER TEMPERATURE detected\nShutting down NOW.\n");
|
||||
shutdown_nice(RB_POWEROFF);
|
||||
}
|
||||
@ -940,7 +940,12 @@ sbus_overtemp(void *arg)
|
||||
static void
|
||||
sbus_pwrfail(void *arg)
|
||||
{
|
||||
static int shutdown;
|
||||
|
||||
/* As the interrupt is cleared we may be called multiple times. */
|
||||
if (shutdown != 0)
|
||||
return;
|
||||
shutdown++;
|
||||
printf("Power failure detected\nShutting down NOW.\n");
|
||||
shutdown_nice(0);
|
||||
}
|
||||
|
@ -136,6 +136,9 @@
|
||||
#define SBR_OBIO_DIAG 0x4808 /* OBIO and misc int state diag reg */
|
||||
#define SBR_STRBUF_DIAG 0x5000 /* Streaming buffer diag regs */
|
||||
|
||||
/* INO defines */
|
||||
#define SBUS_MAX_INO 0x3f
|
||||
|
||||
/* Width of the physical addresses the IOMMU translates to */
|
||||
#define SBUS_IOMMU_BITS 41
|
||||
|
||||
|
@ -2330,9 +2330,6 @@ ENTRY(tl0_intr)
|
||||
stx %i6, [%sp + SPOFF + CCFSZ + TF_O6]
|
||||
stx %i7, [%sp + SPOFF + CCFSZ + TF_O7]
|
||||
|
||||
call critical_enter
|
||||
nop
|
||||
|
||||
SET(intr_handlers, %l1, %l0)
|
||||
sllx %l3, IH_SHIFT, %l1
|
||||
ldx [%l0 + %l1], %l1
|
||||
@ -2340,9 +2337,6 @@ ENTRY(tl0_intr)
|
||||
call %l1
|
||||
add %sp, CCFSZ + SPOFF, %o0
|
||||
|
||||
call critical_exit
|
||||
nop
|
||||
|
||||
/* %l3 contains PIL */
|
||||
SET(intrcnt, %l1, %l2)
|
||||
prefetcha [%l2] ASI_N, 1
|
||||
@ -2847,9 +2841,6 @@ ENTRY(tl1_intr)
|
||||
mov %l5, PCPU_REG
|
||||
wrpr %g0, PSTATE_KERNEL, %pstate
|
||||
|
||||
call critical_enter
|
||||
nop
|
||||
|
||||
SET(intr_handlers, %l5, %l4)
|
||||
sllx %l7, IH_SHIFT, %l5
|
||||
ldx [%l4 + %l5], %l5
|
||||
@ -2857,9 +2848,6 @@ ENTRY(tl1_intr)
|
||||
call %l5
|
||||
add %sp, CCFSZ + SPOFF, %o0
|
||||
|
||||
call critical_exit
|
||||
nop
|
||||
|
||||
/* %l7 contains PIL */
|
||||
SET(intrcnt, %l5, %l4)
|
||||
prefetcha [%l4] ASI_N, 1
|
||||
|
@ -88,7 +88,7 @@ struct intr_vector intr_vectors[IV_MAX];
|
||||
uint16_t intr_countp[IV_MAX];
|
||||
static u_long intr_stray_count[IV_MAX];
|
||||
|
||||
static char *pil_names[] = {
|
||||
static const char *pil_names[] = {
|
||||
"stray",
|
||||
"low", /* PIL_LOW */
|
||||
"ithrd", /* PIL_ITHREAD */
|
||||
@ -99,10 +99,11 @@ static char *pil_names[] = {
|
||||
"fast", /* PIL_FAST */
|
||||
"tick", /* PIL_TICK */
|
||||
};
|
||||
|
||||
|
||||
/* protect the intr_vectors table */
|
||||
static struct mtx intr_table_lock;
|
||||
|
||||
static void intr_enable_eoi(void *);
|
||||
static void intr_execute_handlers(void *);
|
||||
static void intr_stray_level(struct trapframe *);
|
||||
static void intr_stray_vector(void *);
|
||||
@ -219,6 +220,7 @@ intr_init1()
|
||||
intr_vectors[i].iv_arg = &intr_vectors[i];
|
||||
intr_vectors[i].iv_pri = PIL_LOW;
|
||||
intr_vectors[i].iv_vec = i;
|
||||
intr_vectors[i].iv_refcnt = 0;
|
||||
}
|
||||
intr_handlers[PIL_LOW] = intr_fast;
|
||||
}
|
||||
@ -230,17 +232,32 @@ intr_init2()
|
||||
mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN);
|
||||
}
|
||||
|
||||
static void
|
||||
intr_enable_eoi(void *arg)
|
||||
{
|
||||
struct intr_vector *iv;
|
||||
const struct intr_controller *ic;
|
||||
|
||||
iv = arg;
|
||||
ic = iv->iv_ic;
|
||||
ic->ic_enable(iv);
|
||||
ic->ic_eoi(iv);
|
||||
}
|
||||
|
||||
static void
|
||||
intr_execute_handlers(void *cookie)
|
||||
{
|
||||
struct intr_vector *iv;
|
||||
#ifndef INTR_FILTER
|
||||
struct intr_event *ie;
|
||||
struct intr_handler *ih;
|
||||
int error, thread, ret;
|
||||
#endif
|
||||
|
||||
iv = cookie;
|
||||
#ifndef INTR_FILTER
|
||||
ie = iv->iv_event;
|
||||
if (ie == NULL) {
|
||||
if (iv->iv_ic == NULL || ie == NULL) {
|
||||
intr_stray_vector(iv);
|
||||
return;
|
||||
}
|
||||
@ -248,6 +265,7 @@ intr_execute_handlers(void *cookie)
|
||||
/* Execute fast interrupt handlers directly. */
|
||||
ret = 0;
|
||||
thread = 0;
|
||||
critical_enter();
|
||||
TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
|
||||
if (ih->ih_filter == NULL) {
|
||||
thread = 1;
|
||||
@ -266,6 +284,9 @@ intr_execute_handlers(void *cookie)
|
||||
thread = 1;
|
||||
}
|
||||
}
|
||||
if (!thread)
|
||||
intr_enable_eoi(iv);
|
||||
critical_exit();
|
||||
|
||||
/* Schedule a heavyweight interrupt process. */
|
||||
if (thread)
|
||||
@ -275,55 +296,114 @@ intr_execute_handlers(void *cookie)
|
||||
else
|
||||
error = 0;
|
||||
if (error == EINVAL)
|
||||
#else
|
||||
if (intr_event_handle(iv->iv_event, NULL) != 0)
|
||||
#endif
|
||||
intr_stray_vector(iv);
|
||||
}
|
||||
|
||||
int
|
||||
inthand_add(const char *name, int vec, driver_filter_t *filt,
|
||||
void (*handler)(void *), void *arg, int flags, void **cookiep)
|
||||
intr_controller_register(int vec, const struct intr_controller *ic,
|
||||
void *icarg)
|
||||
{
|
||||
struct intr_event *ie;
|
||||
struct intr_vector *iv;
|
||||
struct intr_event *ie; /* descriptor for the IRQ */
|
||||
struct intr_event *orphan;
|
||||
int errcode;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Work around a race where more than one CPU may be registering
|
||||
* handlers on the same IRQ at the same time.
|
||||
*/
|
||||
iv = &intr_vectors[vec];
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
ie = iv->iv_event;
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
if (ie == NULL) {
|
||||
errcode = intr_event_create(&ie, (void *)(intptr_t)vec, 0, NULL,
|
||||
"vec%d:", vec);
|
||||
if (errcode)
|
||||
return (errcode);
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
if (iv->iv_event == NULL) {
|
||||
iv->iv_event = ie;
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
} else {
|
||||
orphan = ie;
|
||||
ie = iv->iv_event;
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
intr_event_destroy(orphan);
|
||||
}
|
||||
if (ie != NULL)
|
||||
return (EEXIST);
|
||||
/*
|
||||
* Testing shows that at least with the interrupt controllers of
|
||||
* Psycho and Schizo bridges enabling an interrupt doesn't cause
|
||||
* an outstanding interrupt to be issued to the CPU. Thus we can't
|
||||
* use a function doing disable+EOI for the "disable" pointer as
|
||||
* done on other architectures because this would lead to a lost
|
||||
* interrupt if it triggers while we are still processing the
|
||||
* previous one. Instead we use an enable+EOI approach because as
|
||||
* outlined in the Tomatillo documentation clearing an interrupt
|
||||
* in the interrupt controller causes it to be (re)issued to the
|
||||
* CPU as long as the source of a level sensitive interrupt is
|
||||
* not cleared.
|
||||
*/
|
||||
error = intr_event_create(&ie, iv, 0, intr_enable_eoi,
|
||||
#ifdef INTR_FILTER
|
||||
ic->ic_eoi, ic->ic_disable, "vec%d:", vec);
|
||||
#else
|
||||
"vec%d:", vec);
|
||||
#endif
|
||||
if (error != 0)
|
||||
return (error);
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
if (iv->iv_event != NULL) {
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
intr_event_destroy(ie);
|
||||
return (EEXIST);
|
||||
}
|
||||
iv->iv_ic = ic;
|
||||
iv->iv_icarg = icarg;
|
||||
iv->iv_event = ie;
|
||||
iv->iv_mid = PCPU_GET(mid);
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
errcode = intr_event_add_handler(ie, name, filt, handler, arg,
|
||||
int
|
||||
inthand_add(const char *name, int vec, driver_filter_t *filt,
|
||||
driver_intr_t *handler, void *arg, int flags, void **cookiep)
|
||||
{
|
||||
const struct intr_controller *ic;
|
||||
struct intr_event *ie;
|
||||
struct intr_handler *ih;
|
||||
struct intr_vector *iv;
|
||||
int error, fast;
|
||||
|
||||
iv = &intr_vectors[vec];
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
ic = iv->iv_ic;
|
||||
ie = iv->iv_event;
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
if (ic == NULL || ie == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
error = intr_event_add_handler(ie, name, filt, handler, arg,
|
||||
intr_priority(flags), flags, cookiep);
|
||||
if (errcode)
|
||||
return (errcode);
|
||||
|
||||
intr_setup(filt != NULL ? PIL_FAST : PIL_ITHREAD, intr_fast, vec,
|
||||
intr_execute_handlers, iv);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
/* Disable the interrupt while we fiddle with it. */
|
||||
ic->ic_disable(iv);
|
||||
iv->iv_refcnt++;
|
||||
if (iv->iv_refcnt == 1)
|
||||
intr_setup(filt != NULL ? PIL_FAST : PIL_ITHREAD, intr_fast,
|
||||
vec, intr_execute_handlers, iv);
|
||||
else if (filt != NULL) {
|
||||
/*
|
||||
* Check if we need to upgrade from PIL_ITHREAD to PIL_FAST.
|
||||
* Given that apart from the on-board SCCs and UARTs shared
|
||||
* interrupts are rather uncommon on sparc64 this sould be
|
||||
* pretty rare in practice.
|
||||
*/
|
||||
fast = 0;
|
||||
TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
|
||||
if (ih->ih_filter != NULL && ih->ih_filter != filt) {
|
||||
fast = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fast == 0)
|
||||
intr_setup(PIL_FAST, intr_fast, vec,
|
||||
intr_execute_handlers, iv);
|
||||
}
|
||||
intr_stray_count[vec] = 0;
|
||||
|
||||
intrcnt_updatename(vec, ie->ie_fullname, 0);
|
||||
|
||||
/* Ensure the interrupt is cleared, it might have triggered before. */
|
||||
intr_enable_eoi(iv);
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -332,22 +412,24 @@ inthand_remove(int vec, void *cookie)
|
||||
{
|
||||
struct intr_vector *iv;
|
||||
int error;
|
||||
|
||||
|
||||
error = intr_event_remove_handler(cookie);
|
||||
if (error == 0) {
|
||||
/*
|
||||
* XXX: maybe this should be done regardless of whether
|
||||
* intr_event_remove_handler() succeeded?
|
||||
* XXX: aren't the PIL's backwards below?
|
||||
*/
|
||||
iv = &intr_vectors[vec];
|
||||
mtx_lock_spin(&intr_table_lock);
|
||||
if (iv->iv_event == NULL)
|
||||
intr_setup(PIL_ITHREAD, intr_fast, vec,
|
||||
intr_stray_vector, iv);
|
||||
else
|
||||
iv->iv_refcnt--;
|
||||
if (iv->iv_refcnt == 0) {
|
||||
/*
|
||||
* Don't disable the interrupt for now, so that
|
||||
* stray interrupts get detected...
|
||||
*/
|
||||
intr_setup(PIL_LOW, intr_fast, vec,
|
||||
intr_execute_handlers, iv);
|
||||
intr_stray_vector, iv);
|
||||
}
|
||||
mtx_unlock_spin(&intr_table_lock);
|
||||
}
|
||||
return (error);
|
||||
|
@ -107,6 +107,7 @@ tick_hardclock(struct trapframe *tf)
|
||||
* the past could be written in the worst case, causing hardclock to
|
||||
* stop.
|
||||
*/
|
||||
critical_enter();
|
||||
adj = PCPU_GET(tickadj);
|
||||
s = intr_disable();
|
||||
tick = rd(tick);
|
||||
@ -137,6 +138,7 @@ tick_hardclock(struct trapframe *tf)
|
||||
}
|
||||
PCPU_SET(tickref, ref);
|
||||
PCPU_SET(tickadj, adj);
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
|
||||
#define UPA_CFG_ESTAR_SPEED_1_64 0x40
|
||||
|
||||
#define UPA_INO_BASE 0x2a
|
||||
#define UPA_INO_MAX 0x2b
|
||||
|
||||
struct upa_regs {
|
||||
uint64_t phys;
|
||||
@ -105,6 +106,8 @@ static bus_probe_nomatch_t upa_probe_nomatch;
|
||||
static bus_get_resource_list_t upa_get_resource_list;
|
||||
static ofw_bus_get_devinfo_t upa_get_devinfo;
|
||||
|
||||
static void upa_intr_enable(void *);
|
||||
static void upa_intr_disable(void *);
|
||||
static struct upa_devinfo *upa_setup_dinfo(device_t, struct upa_softc *,
|
||||
phandle_t, uint32_t);
|
||||
static void upa_destroy_dinfo(struct upa_devinfo *);
|
||||
@ -148,6 +151,18 @@ static devclass_t upa_devclass;
|
||||
DEFINE_CLASS_0(upa, upa_driver, upa_methods, sizeof(struct upa_softc));
|
||||
DRIVER_MODULE(upa, nexus, upa_driver, upa_devclass, 0, 0);
|
||||
|
||||
static const struct intr_controller upa_ic = {
|
||||
upa_intr_enable,
|
||||
upa_intr_disable,
|
||||
/* The interrupts are pulse type and thus automatically cleared. */
|
||||
NULL
|
||||
};
|
||||
|
||||
struct upa_icarg {
|
||||
struct upa_softc *uica_sc;
|
||||
u_int uica_imr;
|
||||
};
|
||||
|
||||
static int
|
||||
upa_probe(device_t dev)
|
||||
{
|
||||
@ -166,15 +181,16 @@ static int
|
||||
upa_attach(device_t dev)
|
||||
{
|
||||
struct upa_devinfo *udi;
|
||||
struct upa_icarg *uica;
|
||||
struct upa_softc *sc;
|
||||
phandle_t child, node;
|
||||
device_t cdev;
|
||||
uint32_t portid;
|
||||
int i, rid;
|
||||
int i, imr, j, rid;
|
||||
#if 1
|
||||
device_t *children, schizo;
|
||||
u_long scount, sstart, ucount, ustart;
|
||||
int j, nchildren;
|
||||
int nchildren;
|
||||
#endif
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
@ -257,11 +273,10 @@ upa_attach(device_t dev)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (OF_getprop(node, "portid", &portid, sizeof(portid)) == -1) {
|
||||
device_printf(dev, "could not determine portid\n");
|
||||
if (OF_getprop(node, "portid", &sc->sc_ign, sizeof(sc->sc_ign)) == -1) {
|
||||
device_printf(dev, "could not determine IGN\n");
|
||||
goto fail;
|
||||
}
|
||||
sc->sc_ign = (portid << INTMAP_IGN_SHIFT) & INTMAP_IGN_MASK;
|
||||
|
||||
sc->sc_nrange = OF_getprop_alloc(node, "ranges", sizeof(*sc->sc_ranges),
|
||||
(void **)&sc->sc_ranges);
|
||||
@ -270,6 +285,37 @@ upa_attach(device_t dev)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hunt through all the interrupt mapping regs and register our
|
||||
* interrupt controller for the corresponding interrupt vectors.
|
||||
*/
|
||||
for (i = UPA_INO_BASE; i <= UPA_INO_MAX; i++) {
|
||||
imr = 0;
|
||||
for (j = UPA_IMR1; j <= UPA_IMR2; j++) {
|
||||
if (INTVEC(UPA_READ(sc, j, 0x0)) ==
|
||||
INTMAP_VEC(sc->sc_ign, i)) {
|
||||
imr = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (imr == 0)
|
||||
continue;
|
||||
uica = malloc(sizeof(*uica), M_DEVBUF, M_NOWAIT);
|
||||
if (uica == NULL)
|
||||
panic("%s: could not allocate interrupt controller "
|
||||
"argument", __func__);
|
||||
uica->uica_sc = sc;
|
||||
uica->uica_imr = imr;
|
||||
#ifdef UPA_DEBUG
|
||||
device_printf(dev, "intr map (INO %d) IMR%d: %#lx\n",
|
||||
i, imr, (u_long)UPA_READ(sc, imr, 0x0));
|
||||
#endif
|
||||
if (intr_controller_register(INTMAP_VEC(sc->sc_ign, i),
|
||||
&upa_ic, uica) != 0)
|
||||
panic("%s: could not register interrupt controller "
|
||||
"for INO %d", __func__, i);
|
||||
}
|
||||
|
||||
/* Make sure the power level is appropriate for normal operation. */
|
||||
if (UPA_READ(sc, UPA_CFG, UPA_CFG_IF) != UPA_CFG_IF_POK) {
|
||||
if (bootverbose)
|
||||
@ -287,9 +333,7 @@ upa_attach(device_t dev)
|
||||
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
|
||||
/*
|
||||
* The `upa-portid' properties of the children are used as
|
||||
* index for the interrupt mapping registers. Thus we also
|
||||
* use it as device unit number in order to save on a unit
|
||||
* member in the devinfo struct.
|
||||
* index for the interrupt mapping registers.
|
||||
* The `upa-portid' properties are also used to make up the
|
||||
* INOs of the children as the values contained in their
|
||||
* `interrupts' properties are bogus.
|
||||
@ -309,7 +353,7 @@ upa_attach(device_t dev)
|
||||
}
|
||||
if ((udi = upa_setup_dinfo(dev, sc, child, portid)) == NULL)
|
||||
continue;
|
||||
if ((cdev = device_add_child(dev, NULL, portid)) == NULL) {
|
||||
if ((cdev = device_add_child(dev, NULL, -1)) == NULL) {
|
||||
device_printf(dev, "<%s>: device_add_child failed\n",
|
||||
udi->udi_obdinfo.obd_name);
|
||||
upa_destroy_dinfo(udi);
|
||||
@ -404,42 +448,46 @@ upa_alloc_resource(device_t dev, device_t child, int type, int *rid,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
upa_intr_enable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct upa_icarg *uica = iv->iv_icarg;
|
||||
|
||||
UPA_WRITE(uica->uica_sc, uica->uica_imr, 0x0,
|
||||
INTMAP_ENABLE(iv->iv_vec, iv->iv_mid));
|
||||
(void)UPA_READ(uica->uica_sc, uica->uica_imr, 0x0);
|
||||
}
|
||||
|
||||
static void
|
||||
upa_intr_disable(void *arg)
|
||||
{
|
||||
struct intr_vector *iv = arg;
|
||||
struct upa_icarg *uica = iv->iv_icarg;
|
||||
|
||||
UPA_WRITE(uica->uica_sc, uica->uica_imr, 0x0, iv->iv_vec);
|
||||
(void)UPA_READ(uica->uica_sc, uica->uica_imr, 0x0);
|
||||
}
|
||||
|
||||
static int
|
||||
upa_setup_intr(device_t dev, device_t child, struct resource *ires, int flags,
|
||||
driver_filter_t *filt, driver_intr_t *func, void *arg, void **cookiep)
|
||||
{
|
||||
struct upa_softc *sc;
|
||||
uint64_t intrmap;
|
||||
u_int imr;
|
||||
int error;
|
||||
u_long vec;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
imr = device_get_unit(child) == 0 ? UPA_IMR1 : UPA_IMR2;
|
||||
intrmap = UPA_READ(sc, imr, 0x0);
|
||||
if (INTVEC(intrmap) != INTVEC(rman_get_start(ires))) {
|
||||
device_printf(dev,
|
||||
"invalid interrupt vector (0x%x != 0x%x)\n",
|
||||
(unsigned int)INTVEC(intrmap),
|
||||
(unsigned int)INTVEC(rman_get_start(ires)));
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* The interrupts are pulse type and therefore automatically
|
||||
* cleared. Thus we don't need to use a stub/wrapper like we
|
||||
* do with the other sun4u host-to-<foo> bridges.
|
||||
*/
|
||||
|
||||
UPA_WRITE(sc, imr, 0x0, intrmap & ~INTMAP_V);
|
||||
(void)UPA_READ(sc, imr, 0x0);
|
||||
error = bus_generic_setup_intr(dev, child, ires, flags, filt,
|
||||
func, arg, cookiep);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
UPA_WRITE(sc, imr, 0x0, INTMAP_ENABLE(intrmap, PCPU_GET(mid)));
|
||||
(void)UPA_READ(sc, imr, 0x0);
|
||||
|
||||
return (0);
|
||||
* Make sure the vector is fully specified and we registered
|
||||
* our interrupt controller for it.
|
||||
*/
|
||||
vec = rman_get_start(ires);
|
||||
if (INTIGN(vec) != sc->sc_ign || intr_vectors[vec].iv_ic != &upa_ic) {
|
||||
device_printf(dev, "invalid interrupt vector 0x%lx\n", vec);
|
||||
return (EINVAL);
|
||||
}
|
||||
return (bus_generic_setup_intr(dev, child, ires, flags, filt, func,
|
||||
arg, cookiep));
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
@ -487,7 +535,7 @@ upa_setup_dinfo(device_t dev, struct upa_softc *sc, phandle_t node,
|
||||
reg[i].phys + reg[i].size - 1, reg[i].size);
|
||||
free(reg, M_OFWPROP);
|
||||
|
||||
intr = (UPA_INO_BASE + portid) | sc->sc_ign;
|
||||
intr = INTMAP_VEC(sc->sc_ign, (UPA_INO_BASE + portid));
|
||||
resource_list_add(&udi->udi_rl, SYS_RES_IRQ, 0, intr, intr, 1);
|
||||
|
||||
return (udi);
|
||||
|
Loading…
x
Reference in New Issue
Block a user