1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Rework the handling of interrupt handlers for children of ppc and ppbus:

- Retire IVARs for passing IRQs around.  Instead, ppbus and ppc now allow
  child devices to access the interrupt by via a rid 0 IRQ resource
  using bus_alloc_resource_any().
- ppc creates its own interrupt event to manage the interrupt handlers of
  child devices.  ppc does not allow child devices to use filters.  It
  could allow this if needed, but none of the current drivers use them
  and it adds a good bit of complication.  It uses
  intr_event_execute_handlers() to fire the child device interrupt handlers
  from its threaded interrupt handler.
- Remove the ppbus_dummy_intr() hack.  Now the ppc device always has an
  interrupt handler registered and we no longer bounce all the way up to
  nexus to manage adding/removing ppbus child interrupt handlers.  Instead,
  the child handlers are added and removed to the private interrupt event
  in the ppc device.
This commit is contained in:
John Baldwin 2008-09-15 22:26:32 +00:00
parent 37e9511fcb
commit ca3d37955c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183053
13 changed files with 146 additions and 163 deletions

View File

@ -193,33 +193,6 @@ lp_identify(driver_t *driver, device_t parent)
static int
lp_probe(device_t dev)
{
device_t ppbus = device_get_parent(dev);
struct lp_data *lp;
int zero = 0;
uintptr_t irq;
lp = DEVTOSOFTC(dev);
/* retrieve the ppbus irq */
BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
/* if we haven't interrupts, the probe fails */
if (irq == -1) {
device_printf(dev, "not an interrupt driven port, failed.\n");
return (ENXIO);
}
/* reserve the interrupt resource, expecting irq is available to continue */
lp->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
RF_SHAREABLE);
if (lp->res_irq == 0) {
device_printf(dev, "cannot reserve interrupt, failed.\n");
return (ENXIO);
}
/*
* lp dependent initialisation.
*/
device_set_desc(dev, "PLIP network interface");
@ -231,6 +204,18 @@ lp_attach (device_t dev)
{
struct lp_data *lp = DEVTOSOFTC(dev);
struct ifnet *ifp;
int rid = 0;
/*
* Reserve the interrupt resource. If we don't have one, the
* attach fails.
*/
lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE);
if (lp->res_irq == 0) {
device_printf(dev, "cannot reserve interrupt, failed.\n");
return (ENXIO);
}
ifp = lp->sc_ifp = if_alloc(IFT_PARA);
if (ifp == NULL) {

View File

@ -367,9 +367,8 @@ lpt_attach(device_t dev)
{
device_t ppbus = device_get_parent(dev);
struct lpt_data *sc = DEVTOSOFTC(dev);
int zero = 0, unit = device_get_unit(dev);
int rid = 0, unit = device_get_unit(dev);
int error;
intptr_t irq;
sc->sc_primed = 0; /* not primed yet */
@ -383,14 +382,9 @@ lpt_attach(device_t dev)
/* check if we can use interrupt, should be done by ppc stuff */
lprintf(("oldirq %x\n", sc->sc_irq));
/* retrieve the ppbus irq */
BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
if (irq > 0) {
/* declare our interrupt handler */
sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
&zero, irq, irq, 1, RF_SHAREABLE);
}
/* declare our interrupt handler */
sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE);
if (sc->intr_resource) {
sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
device_printf(dev, "Interrupt-driven port\n");
@ -398,7 +392,7 @@ lpt_attach(device_t dev)
sc->sc_irq = 0;
device_printf(dev, "Polled port\n");
}
lprintf(("irq %x %x\n", (int)irq, sc->sc_irq));
lprintf(("irq %x\n", sc->sc_irq));
lpt_release_ppbus(dev);

View File

@ -121,9 +121,6 @@ ppbus_read_ivar(device_t bus, device_t dev, int index, uintptr_t* val)
case PPBUS_IVAR_AVM:
*val = (u_long)ppbdev->avm;
break;
case PPBUS_IVAR_IRQ:
BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_IRQ, val);
break;
default:
return (ENOENT);
}
@ -383,38 +380,9 @@ ppb_scan_bus(device_t bus)
#endif /* !DONTPROBE_1284 */
static void
ppbus_dummy_intr(void *arg)
{
}
static int
ppbus_attach(device_t dev)
{
struct ppb_data *ppb = (struct ppb_data *)device_get_softc(dev);
uintptr_t irq;
int error, rid;
/* Attach a dummy interrupt handler to suck up any stray interrupts. */
BUS_READ_IVAR(device_get_parent(dev), dev, PPC_IVAR_IRQ, &irq);
if (irq > 0) {
rid = 0;
ppb->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, irq,
irq, 1, RF_SHAREABLE);
if (ppb->irq_res != NULL) {
error = bus_setup_intr(dev, ppb->irq_res,
INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppbus_dummy_intr,
ppb, &ppb->intr_cookie);
if (error) {
device_printf(dev,
"failed to setup interrupt handler\n");
bus_release_resource(dev, SYS_RES_IRQ, 0,
ppb->irq_res);
return (error);
}
}
}
/* Locate our children */
bus_generic_probe(dev);
@ -433,7 +401,6 @@ ppbus_attach(device_t dev)
static int
ppbus_detach(device_t dev)
{
struct ppb_data *ppb = (struct ppb_data *)device_get_softc(dev);
device_t *children;
int nchildren, i;
@ -445,10 +412,6 @@ ppbus_detach(device_t dev)
free(children, M_TEMP);
}
if (ppb->irq_res != NULL) {
bus_teardown_intr(dev, ppb->irq_res, ppb->intr_cookie);
bus_release_resource(dev, SYS_RES_IRQ, 0, ppb->irq_res);
}
return (0);
}
@ -602,7 +565,8 @@ static device_method_t ppbus_methods[] = {
DEVMETHOD(bus_write_ivar, ppbus_write_ivar),
DEVMETHOD(bus_setup_intr, ppbus_setup_intr),
DEVMETHOD(bus_teardown_intr, ppbus_teardown_intr),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
{ 0, 0 }
};

View File

@ -179,7 +179,6 @@ struct ppb_context {
*/
#define PPBUS_IVAR_MODE 0
#define PPBUS_IVAR_AVM 1
#define PPBUS_IVAR_IRQ 2
/* other fields are reserved to the ppbus internals */
@ -216,7 +215,6 @@ struct ppb_device {
/* Parallel Port Chipset IVARS */ /* elsewhere XXX */
#define PPC_IVAR_EPP_PROTO 0
#define PPC_IVAR_IRQ 1
/*
* Maximum size of the PnP info string
@ -248,9 +246,6 @@ struct ppb_data {
* NIBBLE, PS2, EPP or ECP */
void *ppb_owner; /* device which owns the bus */
struct resource *irq_res;
void *intr_cookie;
};
#ifdef _KERNEL

View File

@ -163,16 +163,12 @@ static int
ppi_attach(device_t dev)
{
#ifdef PERIPH_1284
uintptr_t irq;
int zero = 0;
int rid = 0;
struct ppi_data *ppi = DEVTOSOFTC(dev);
/* retrive the irq */
BUS_READ_IVAR(device_get_parent(dev), dev, PPBUS_IVAR_IRQ, &irq);
/* declare our interrupt handler */
ppi->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
&zero, irq, irq, 1, RF_ACTIVE);
ppi->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE);
#endif /* PERIPH_1284 */
make_dev(&ppi_cdevsw, device_get_unit(dev), /* XXX cleanup */

View File

@ -107,18 +107,14 @@ ppsattach(device_t dev)
struct pps_data *sc = DEVTOSOFTC(dev);
device_t ppbus = device_get_parent(dev);
struct cdev *d;
intptr_t irq;
int i, unit, zero = 0;
int i, unit, rid = 0;
mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN);
/* retrieve the ppbus irq */
BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
if (irq > 0) {
/* declare our interrupt handler */
sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
&zero, irq, irq, 1, RF_SHAREABLE);
}
/* declare our interrupt handler */
sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE);
/* interrupts seem mandatory */
if (sc->intr_resource == NULL)
return (ENXIO);

View File

@ -32,10 +32,12 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/interrupt.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <machine/bus.h>
#include <machine/resource.h>
@ -1515,10 +1517,21 @@ ppc_exec_microseq(device_t dev, struct ppb_microseq **p_msq)
static void
ppcintr(void *arg)
{
device_t dev = (device_t)arg;
struct ppc_data *ppc = (struct ppc_data *)device_get_softc(dev);
struct ppc_data *ppc = arg;
u_char ctr, ecr, str;
/*
* If we have any child interrupt handlers registered, let
* them handle this interrupt.
*
* XXX: If DMA is in progress should we just complete that w/o
* doing this?
*/
if (ppc->ppc_child_handlers > 0) {
intr_event_execute_handlers(curproc, ppc->ppc_intr_event);
return;
}
str = r_str(ppc);
ctr = r_ctr(ppc);
ecr = r_ecr(ppc);
@ -1790,8 +1803,8 @@ int
ppc_attach(device_t dev)
{
struct ppc_data *ppc = DEVTOSOFTC(dev);
device_t ppbus;
int error;
device_printf(dev, "%s chipset (%s) in %s mode%s\n",
ppc_models[ppc->ppc_model], ppc_avms[ppc->ppc_avm],
@ -1802,6 +1815,30 @@ ppc_attach(device_t dev)
device_printf(dev, "FIFO with %d/%d/%d bytes threshold\n",
ppc->ppc_fifo, ppc->ppc_wthr, ppc->ppc_rthr);
if (ppc->res_irq) {
/*
* Create an interrupt event to manage the handlers of
* child devices.
*/
error = intr_event_create(&ppc->ppc_intr_event, ppc, 0, -1,
NULL, NULL, NULL, NULL, "%s:", device_get_nameunit(dev));
if (error) {
device_printf(dev,
"failed to create interrupt event: %d\n", error);
return (error);
}
/* default to the tty mask for registration */ /* XXX */
error = bus_setup_intr(dev, ppc->res_irq, INTR_TYPE_TTY,
NULL, ppcintr, ppc, &ppc->intr_cookie);
if (error) {
device_printf(dev,
"failed to register interrupt handler: %d\n",
error);
return (error);
}
}
/* add ppbus as a child of this isa to parallel bridge */
ppbus = device_add_child(dev, "ppbus", -1);
@ -1810,17 +1847,6 @@ ppc_attach(device_t dev)
*/
device_probe_and_attach(ppbus);
/* register the ppc interrupt handler as default */
if (ppc->res_irq) {
/* default to the tty mask for registration */ /* XXX */
if (bus_setup_intr(dev, ppc->res_irq, INTR_TYPE_TTY,
NULL, ppcintr, dev, &ppc->intr_cookie) == 0) {
/* remember the ppcintr is registered */
ppc->ppc_registered = 1;
}
}
return (0);
}
@ -1935,9 +1961,6 @@ ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val)
case PPC_IVAR_EPP_PROTO:
*val = (u_long)ppc->ppc_epp;
break;
case PPC_IVAR_IRQ:
*val = (u_long)ppc->ppc_irq;
break;
default:
return (ENOENT);
}
@ -1946,63 +1969,84 @@ ppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val)
}
/*
* Resource is useless here since ppbus devices' interrupt handlers are
* multiplexed to the same resource initially allocated by ppc
* We allow child devices to allocate an IRQ resource at rid 0 for their
* interrupt handlers.
*/
struct resource *
ppc_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 ppc_data *ppc = DEVTOSOFTC(bus);
switch (type) {
case SYS_RES_IRQ:
if (*rid == 0)
return (ppc->res_irq);
break;
}
return (NULL);
}
int
ppc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
#ifdef INVARIANTS
struct ppc_data *ppc = DEVTOSOFTC(bus);
#endif
switch (type) {
case SYS_RES_IRQ:
if (rid == 0) {
KASSERT(r == ppc->res_irq,
("ppc child IRQ resource mismatch"));
return (0);
}
break;
}
return (EINVAL);
}
/*
* If a child wants to add a handler for our IRQ, add it to our interrupt
* event. Otherwise, fail the request.
*/
int
ppc_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep)
{
int error;
struct ppc_data *ppc = DEVTOSOFTC(bus);
int error;
if (ppc->ppc_registered) {
/* XXX refuse registration if DMA is in progress */
if (r != ppc->res_irq)
return (EINVAL);
/* first, unregister the default interrupt handler */
if ((error = BUS_TEARDOWN_INTR(device_get_parent(bus),
bus, ppc->res_irq, ppc->intr_cookie)))
return (error);
/* We don't allow filters. */
if (filt != NULL)
return (EINVAL);
/* bus_deactivate_resource(bus, SYS_RES_IRQ, ppc->rid_irq, */
/* ppc->res_irq); */
/* DMA/FIFO operation won't be possible anymore */
ppc->ppc_registered = 0;
}
/*
* pass registration to the upper layer, ignore the incoming
* resource
*/
return (BUS_SETUP_INTR(device_get_parent(bus), child,
r, flags, filt, ihand, arg, cookiep));
error = intr_event_add_handler(ppc->ppc_intr_event,
device_get_nameunit(child), NULL, ihand, arg, intr_priority(flags),
flags, cookiep);
if (error == 0)
ppc->ppc_child_handlers++;
return (error);
}
/*
* When no underlying device has a registered interrupt, register the ppc
* layer one
*/
int
ppc_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
ppc_teardown_intr(device_t bus, device_t child, struct resource *r, void *cookie)
{
int error;
struct ppc_data *ppc = DEVTOSOFTC(bus);
device_t parent = device_get_parent(bus);
int error;
/* pass unregistration to the upper layer */
if ((error = BUS_TEARDOWN_INTR(parent, child, r, ih)))
return (error);
/* default to the tty mask for registration */ /* XXX */
if (ppc->ppc_irq &&
!(error = BUS_SETUP_INTR(parent, bus, ppc->res_irq,
INTR_TYPE_TTY, NULL, ppcintr, bus, &ppc->intr_cookie))) {
/* remember the ppcintr is registered */
ppc->ppc_registered = 1;
}
if (r != ppc->res_irq)
return (EINVAL);
KASSERT(intr_handler_source(cookie) == ppc,
("ppc_teardown_intr: source mismatch"));
error = intr_event_remove_handler(cookie);
if (error == 0)
ppc->ppc_child_handlers--;
return (error);
}

View File

@ -65,7 +65,8 @@ static device_method_t ppc_acpi_methods[] = {
DEVMETHOD(bus_read_ivar, ppc_read_ivar),
DEVMETHOD(bus_setup_intr, ppc_setup_intr),
DEVMETHOD(bus_teardown_intr, ppc_teardown_intr),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_alloc_resource, ppc_alloc_resource),
DEVMETHOD(bus_release_resource, ppc_release_resource),
/* ppbus interface */
DEVMETHOD(ppbus_io, ppc_io),

View File

@ -62,7 +62,8 @@ static device_method_t ppc_isa_methods[] = {
DEVMETHOD(bus_read_ivar, ppc_read_ivar),
DEVMETHOD(bus_setup_intr, ppc_setup_intr),
DEVMETHOD(bus_teardown_intr, ppc_teardown_intr),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_alloc_resource, ppc_alloc_resource),
DEVMETHOD(bus_release_resource, ppc_release_resource),
/* ppbus interface */
DEVMETHOD(ppbus_io, ppc_io),
@ -142,7 +143,7 @@ ppc_isa_write(device_t dev, char *buf, int len, int how)
int s, error = 0;
int spin;
if (!(ppc->ppc_avm & PPB_ECP) || !ppc->ppc_registered)
if (!(ppc->ppc_avm & PPB_ECP))
return (EINVAL);
if (ppc->ppc_dmachan == 0)
return (EINVAL);

View File

@ -55,7 +55,8 @@ static device_method_t ppc_pci_methods[] = {
DEVMETHOD(bus_read_ivar, ppc_read_ivar),
DEVMETHOD(bus_setup_intr, ppc_setup_intr),
DEVMETHOD(bus_teardown_intr, ppc_teardown_intr),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_alloc_resource, ppc_alloc_resource),
DEVMETHOD(bus_release_resource, ppc_release_resource),
/* ppbus interface */
DEVMETHOD(ppbus_io, ppc_io),

View File

@ -57,7 +57,8 @@ static device_method_t ppc_puc_methods[] = {
DEVMETHOD(bus_read_ivar, ppc_read_ivar),
DEVMETHOD(bus_setup_intr, ppc_setup_intr),
DEVMETHOD(bus_teardown_intr, ppc_teardown_intr),
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
DEVMETHOD(bus_alloc_resource, ppc_alloc_resource),
DEVMETHOD(bus_release_resource, ppc_release_resource),
/* ppbus interface */
DEVMETHOD(ppbus_io, ppc_io),

View File

@ -109,7 +109,8 @@ struct ppc_data {
void *intr_cookie;
int ppc_registered; /* 1 if ppcintr() is the registered interrupt */
struct intr_event *ppc_intr_event;
int ppc_child_handlers;
};
/*

View File

@ -42,6 +42,10 @@ int ppc_exec_microseq(device_t, struct ppb_microseq **);
int ppc_setup_intr(device_t, device_t, struct resource *, int,
driver_filter_t *filt, void (*)(void *), void *, void **);
int ppc_teardown_intr(device_t, device_t, struct resource *, void *);
struct resource *ppc_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 ppc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
void ppc_reset_epp(device_t);
void ppc_ecp_sync(device_t);
int ppc_setmode(device_t, int);