Index: powerpc/powerpc/pic_if.m =================================================================== --- powerpc/powerpc/pic_if.m (revision 206272) +++ powerpc/powerpc/pic_if.m (working copy) @@ -60,6 +60,10 @@ u_int cpu; }; +METHOD uint32_t id { + device_t dev; +}; + METHOD void mask { device_t dev; u_int irq; Index: powerpc/powerpc/mp_machdep.c =================================================================== --- powerpc/powerpc/mp_machdep.c (revision 206272) +++ powerpc/powerpc/mp_machdep.c (working copy) @@ -319,7 +319,7 @@ pc, pc->pc_cpuid, ipi); atomic_set_32(&pc->pc_ipimask, (1 << ipi)); - PIC_IPI(pic, pc->pc_cpuid); + PIC_IPI(root_pic, pc->pc_cpuid); CTR1(KTR_SMP, "%s: sent", __func__); } Index: powerpc/powerpc/openpic.c =================================================================== --- powerpc/powerpc/openpic.c (revision 206272) +++ powerpc/powerpc/openpic.c (working copy) @@ -52,6 +52,7 @@ /* * Local routines */ +static int openpic_intr(void *arg); static __inline uint32_t openpic_read(struct openpic_softc *sc, u_int reg) @@ -100,6 +101,19 @@ sc->sc_bt = rman_get_bustag(sc->sc_memr); sc->sc_bh = rman_get_bushandle(sc->sc_memr); + /* Check if this is a cascaded PIC */ + sc->sc_irq = 0; + sc->sc_intr = NULL; + if (resource_list_find(BUS_GET_RESOURCE_LIST(device_get_parent(dev), + dev), SYS_RES_IRQ, 0) != NULL) { + sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq, RF_ACTIVE); + + /* XXX Cascaded PICs pass NULL trapframes! */ + bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_MISC | INTR_MPSAFE, + openpic_intr, NULL, dev, &sc->sc_icookie); + } + x = openpic_read(sc, OPENPIC_FEATURE); switch (x & OPENPIC_FEATURE_VERSION_MASK) { case 1: @@ -175,6 +189,10 @@ powerpc_register_pic(dev, sc->sc_nirq); + /* If this is not a cascaded PIC, it must be the root PIC */ + if (sc->sc_intr == NULL) + root_pic = dev; + return (0); } @@ -202,6 +220,17 @@ openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } +static int +openpic_intr(void *arg) +{ + device_t dev = (device_t)(arg); + + /* XXX Cascaded PICs do not pass non-NULL trapframes! */ + openpic_dispatch(dev, NULL); + + return (FILTER_HANDLED); +} + void openpic_dispatch(device_t dev, struct trapframe *tf) { Index: powerpc/powerpc/intr_machdep.c =================================================================== --- powerpc/powerpc/intr_machdep.c (revision 206272) +++ powerpc/powerpc/intr_machdep.c (working copy) @@ -85,14 +85,6 @@ #include "pic_if.h" -#ifdef MPC85XX -#define ISA_IRQ_COUNT 16 -#endif - -#ifndef ISA_IRQ_COUNT -#define ISA_IRQ_COUNT 0 -#endif - #define MAX_STRAY_LOG 5 MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data"); @@ -108,20 +100,25 @@ enum intr_polarity pol; }; +struct pic { + device_t pic; + uint32_t pic_id; + int ipi_irq; +}; + static struct mtx intr_table_lock; static struct powerpc_intr *powerpc_intrs[INTR_VECTORS]; +static struct pic piclist[MAX_PICS]; static u_int nvectors; /* Allocated vectors */ +static u_int npics; /* PICs registered */ static u_int stray_count; +device_t root_pic; + #ifdef SMP static void *ipi_cookie; #endif -static u_int ipi_irq; - -device_t pic; -device_t pic8259; - static void intr_init(void *dummy __unused) { @@ -197,21 +194,13 @@ powerpc_map_irq(struct powerpc_intr *i) { -#if ISA_IRQ_COUNT > 0 - if (i->irq < ISA_IRQ_COUNT) { - if (pic8259 == NULL) { - i->pic = pic; - i->intline = 0; - return (ENXIO); - } - i->pic = pic8259; - i->intline = i->irq; - return (0); - } -#endif + i->intline = INTR_INTLINE(i->irq); + i->pic = piclist[INTR_IGN(i->irq)].pic; - i->pic = pic; - i->intline = i->irq - ISA_IRQ_COUNT; + /* Try a best guess if that failed */ + if (i->pic == NULL) + i->pic = root_pic; + return (0); } @@ -242,34 +231,70 @@ void powerpc_register_pic(device_t dev, u_int ipi) { + int i; - pic = dev; - ipi_irq = ipi + ISA_IRQ_COUNT; + mtx_lock(&intr_table_lock); + + for (i = 0; i < npics; i++) { + if (piclist[i].pic_id == PIC_ID(dev)) + break; + } + piclist[i].pic = dev; + piclist[i].pic_id = PIC_ID(dev); + piclist[i].ipi_irq = ipi; + if (i == npics) + npics++; + + mtx_unlock(&intr_table_lock); } -void -powerpc_register_8259(device_t dev) +int +powerpc_ign_lookup(uint32_t pic_id) { + int i; - pic8259 = dev; + mtx_lock(&intr_table_lock); + + for (i = 0; i < npics; i++) { + if (piclist[i].pic_id == pic_id) { + mtx_unlock(&intr_table_lock); + return (i); + } + } + piclist[i].pic = NULL; + piclist[i].pic_id = pic_id; + piclist[i].ipi_irq = 0; + npics++; + + mtx_unlock(&intr_table_lock); + + return (i); } int powerpc_enable_intr(void) { struct powerpc_intr *i; - int error, vector; + int error, vector, n; - if (pic == NULL) + if (npics == 0) panic("no PIC detected\n"); #ifdef SMP /* Install an IPI handler. */ - error = powerpc_setup_intr("IPI", ipi_irq, powerpc_ipi_handler, - NULL, NULL, INTR_TYPE_MISC | INTR_EXCL | INTR_FAST, &ipi_cookie); - if (error) { - printf("unable to setup IPI handler\n"); - return (error); + + for (n = 0; n < npics; n++) { + if (piclist[n].pic != root_pic) + continue; + + error = powerpc_setup_intr("IPI", + INTR_VEC(piclist[n].pic_id, piclist[n].ipi_irq), + powerpc_ipi_handler, NULL, NULL, + INTR_TYPE_MISC | INTR_EXCL | INTR_FAST, &ipi_cookie); + if (error) { + printf("unable to setup IPI handler\n"); + return (error); + } } #endif @@ -294,7 +319,7 @@ } int -powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter, +powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) { struct powerpc_intr *i; Index: powerpc/booke/interrupt.c =================================================================== --- powerpc/booke/interrupt.c (revision 206272) +++ powerpc/booke/interrupt.c (working copy) @@ -134,7 +134,7 @@ { critical_enter(); - PIC_DISPATCH(pic, framep); + PIC_DISPATCH(root_pic, framep); critical_exit(); framep->srr1 &= ~PSL_WE; } Index: powerpc/include/intr_machdep.h =================================================================== --- powerpc/include/intr_machdep.h (revision 206272) +++ powerpc/include/intr_machdep.h (working copy) @@ -29,21 +29,27 @@ #define _MACHINE_INTR_MACHDEP_H_ #define INTR_VECTORS 256 +#define MAX_PICS 5 -extern device_t pic; -extern device_t pic8259; +#define IGN_SHIFT 8 +#define INTR_INTLINE(irq) (irq & ((1 << IGN_SHIFT) - 1)) +#define INTR_IGN(irq) (irq >> IGN_SHIFT) +#define INTR_VEC(pic_id, irq) ((powerpc_ign_lookup(pic_id) << IGN_SHIFT) | irq) + +extern device_t root_pic; + struct trapframe; driver_filter_t powerpc_ipi_handler; void powerpc_register_pic(device_t, u_int); -void powerpc_register_8259(device_t); +int powerpc_ign_lookup(uint32_t pic_id); void powerpc_dispatch_intr(u_int, struct trapframe *); int powerpc_enable_intr(void); -int powerpc_setup_intr(const char *, u_int, driver_filter_t, - driver_intr_t, void *, enum intr_type, void **); +int powerpc_setup_intr(const char *, u_int, driver_filter_t, driver_intr_t, + void *, enum intr_type, void **); int powerpc_teardown_intr(void *); int powerpc_config_intr(int, enum intr_trigger, enum intr_polarity); Index: powerpc/include/openpicvar.h =================================================================== --- powerpc/include/openpicvar.h (revision 206272) +++ powerpc/include/openpicvar.h (working copy) @@ -35,10 +35,13 @@ struct openpic_softc { device_t sc_dev; struct resource *sc_memr; + struct resource *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; char *sc_version; int sc_rid; + int sc_irq; + void *sc_icookie; u_int sc_ncpu; u_int sc_nirq; int sc_psim; Index: powerpc/aim/nexus.c =================================================================== --- powerpc/aim/nexus.c (revision 206272) +++ powerpc/aim/nexus.c (working copy) @@ -206,7 +206,7 @@ sc = device_get_softc(dev); start = 0; - end = INTR_VECTORS - 1; + end = MAX_PICS*INTR_VECTORS - 1; sc->sc_rman.rm_start = start; sc->sc_rman.rm_end = end; Index: powerpc/aim/interrupt.c =================================================================== --- powerpc/aim/interrupt.c (revision 206272) +++ powerpc/aim/interrupt.c (working copy) @@ -81,7 +81,7 @@ switch (framep->exc) { case EXC_EXI: critical_enter(); - PIC_DISPATCH(pic, framep); + PIC_DISPATCH(root_pic, framep); critical_exit(); break; Index: powerpc/ofw/ofw_pcibus.c =================================================================== --- powerpc/ofw/ofw_pcibus.c (revision 206272) +++ powerpc/ofw/ofw_pcibus.c (working copy) @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -198,10 +199,15 @@ * resource list. */ if (dinfo->opd_dinfo.cfg.intpin == 0) { + phandle_t iparent; ofw_pci_intr_t intr; if (OF_getprop(child, "interrupts", &intr, sizeof(intr)) > 0) { + if (OF_getprop(child, "interrupt-parent", + &iparent, sizeof(iparent)) > 0) + intr = INTR_VEC(iparent, intr); + resource_list_add(&dinfo->opd_dinfo.resources, SYS_RES_IRQ, 0, intr, intr, 1); } @@ -276,7 +282,7 @@ ofw_pcibus_assign_interrupt(device_t dev, device_t child) { ofw_pci_intr_t intr; - phandle_t node; + phandle_t node, iparent; int isz; node = ofw_bus_get_node(child); @@ -286,8 +292,8 @@ /* * XXX: Right now we don't have anything sensible to do here, - * since the ofw_imap stuff relies on nodes have a reg - * property. There exists ways around this, so the ePAPR + * since the ofw_imap stuff relies on nodes having a reg + * property. There exist ways around this, so the ePAPR * spec will need to be studied. */ @@ -301,18 +307,30 @@ } /* + * Try to determine the node's interrupt parent so we know which + * PIC to use. + */ + + if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < + sizeof(iparent)) + iparent = -1; + + /* * Any AAPL,interrupts property gets priority and is * fully specified (i.e. does not need routing) */ isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); if (isz == sizeof(intr)) { - return (intr); + return ((iparent == -1) ? intr : INTR_VEC(iparent, intr)); } isz = OF_getprop(node, "interrupts", &intr, sizeof(intr)); - if (isz != sizeof(intr)) { - /* No property; our best guess is the intpin. */ + if (isz == sizeof(intr)) { + if (iparent != -1) + intr = INTR_VEC(iparent, intr); + } else { + /* No property: our best guess is the intpin. */ intr = pci_get_intpin(child); } Index: powerpc/ofw/ofw_pcib_pci.c =================================================================== --- powerpc/ofw/ofw_pcib_pci.c (revision 206272) +++ powerpc/ofw/ofw_pcib_pci.c (working copy) @@ -42,6 +42,8 @@ #include #include +#include + #include "pcib_if.h" static int ofw_pcib_pci_probe(device_t bus); @@ -149,6 +151,7 @@ struct ofw_bus_iinfo *ii; struct ofw_pci_register reg; cell_t pintr, mintr; + phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bridge); @@ -157,13 +160,13 @@ pintr = intpin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), - maskbuf)) { + &iparent, maskbuf)) { /* * If we've found a mapping, return it and don't map * it again on higher levels - that causes problems * in some cases, and never seems to be required. */ - return (mintr); + return (INTR_VEC(iparent, mintr)); } } else if (intpin >= 1 && intpin <= 4) { /* Index: powerpc/powermac/cpcht.c =================================================================== --- powerpc/powermac/cpcht.c (revision 206272) +++ powerpc/powermac/cpcht.c (working copy) @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -606,13 +607,15 @@ struct cpcpci_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr; + phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bus); pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, - sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf)) - return (mintr); + sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), + &iparent, maskbuf)) + return (INTR_VEC(iparent, mintr)); /* Maybe it's a real interrupt, not an intpin */ if (pin > 4) Index: powerpc/powermac/openpic_macio.c =================================================================== --- powerpc/powermac/openpic_macio.c (revision 206272) +++ powerpc/powermac/openpic_macio.c (working copy) @@ -59,6 +59,7 @@ * MacIO interface */ static int openpic_macio_probe(device_t); +static uint32_t openpic_macio_id(device_t); static device_method_t openpic_macio_methods[] = { /* Device interface */ @@ -73,6 +74,7 @@ DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_unmask), + DEVMETHOD(pic_id, openpic_macio_id), { 0, 0 }, }; @@ -84,6 +86,7 @@ }; DRIVER_MODULE(openpic, macio, openpic_macio_driver, openpic_devclass, 0, 0); +DRIVER_MODULE(openpic, unin, openpic_macio_driver, openpic_devclass, 0, 0); static int openpic_macio_probe(device_t dev) @@ -96,3 +99,10 @@ device_set_desc(dev, OPENPIC_DEVSTR); return (0); } + +static uint32_t +openpic_macio_id(device_t dev) +{ + return (ofw_bus_get_node(dev)); +} + Index: powerpc/powermac/uninorth.c =================================================================== --- powerpc/powermac/uninorth.c (revision 206272) +++ powerpc/powermac/uninorth.c (working copy) @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -94,7 +95,8 @@ */ static int uninorth_enable_config(struct uninorth_softc *, u_int, u_int, u_int, u_int); -static void unin_enable_gmac(void); +static void unin_enable_gmac(device_t dev); +static void unin_enable_mpic(device_t dev); /* * Driver methods. @@ -164,7 +166,7 @@ { struct uninorth_softc *sc; const char *compatible; - phandle_t node, child; + phandle_t node; u_int32_t reg[2], busrange[2]; struct uninorth_range *rp, *io, *mem[2]; int nmem, i, error; @@ -280,20 +282,6 @@ } } - /* - * Enable the GMAC Ethernet cell if Open Firmware says it is - * used. - */ - for (child = OF_child(node); child; child = OF_peer(child)) { - char compat[32]; - - memset(compat, 0, sizeof(compat)); - OF_getprop(child, "compatible", compat, sizeof(compat)); - if (strcmp(compat, "gmac") == 0) { - unin_enable_gmac(); - } - } - ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); device_add_child(dev, "pci", device_get_unit(dev)); @@ -365,13 +353,15 @@ struct uninorth_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr; + phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bus); pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, - sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf)) - return (mintr); + sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), + &iparent, maskbuf)) + return (INTR_VEC(iparent, mintr)); /* Maybe it's a real interrupt, not an intpin */ if (pin > 4) @@ -581,26 +571,174 @@ /* - * Small stub driver for the Uninorth chip itself, to allow setting - * of various parameters and cell enables + * Driver for the Uninorth chip itself. */ -static struct unin_chip_softc *uncsc; +static MALLOC_DEFINE(M_UNIN, "unin", "unin device information"); + +/* + * Device interface. + */ + +static int unin_chip_probe(device_t); +static int unin_chip_attach(device_t); + +/* + * Bus interface. + */ +static int unin_chip_print_child(device_t dev, device_t child); +static void unin_chip_probe_nomatch(device_t, device_t); +static struct resource *unin_chip_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int unin_chip_activate_resource(device_t, device_t, int, int, + struct resource *); +static int unin_chip_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int unin_chip_release_resource(device_t, device_t, int, int, + struct resource *); +static struct resource_list *unin_chip_get_resource_list (device_t, device_t); + +/* + * OFW Bus interface + */ + +static ofw_bus_get_devinfo_t unin_chip_get_devinfo; + +/* + * Driver methods. + */ +static device_method_t unin_chip_methods[] = { + + /* Device interface */ + DEVMETHOD(device_probe, unin_chip_probe), + DEVMETHOD(device_attach, unin_chip_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, unin_chip_print_child), + DEVMETHOD(bus_probe_nomatch, unin_chip_probe_nomatch), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + DEVMETHOD(bus_alloc_resource, unin_chip_alloc_resource), + DEVMETHOD(bus_release_resource, unin_chip_release_resource), + DEVMETHOD(bus_activate_resource, unin_chip_activate_resource), + DEVMETHOD(bus_deactivate_resource, unin_chip_deactivate_resource), + DEVMETHOD(bus_get_resource_list, unin_chip_get_resource_list), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, unin_chip_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), + + { 0, 0 } +}; + +static driver_t unin_chip_driver = { + "unin", + unin_chip_methods, + sizeof(struct unin_chip_softc) +}; + +static devclass_t unin_chip_devclass; + +DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0); + +/* + * Add an interrupt to the dev's resource list if present + */ static void -unin_enable_gmac(void) +unin_chip_add_intr(phandle_t devnode, struct unin_chip_devinfo *dinfo) { + phandle_t iparent; + int *intr; + int i, nintr; + int icells; + + if (dinfo->udi_ninterrupts >= 6) { + printf("unin: device has more than 6 interrupts\n"); + return; + } + + nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr == -1) { + nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", + sizeof(*intr), (void **)&intr); + if (nintr == -1) + return; + } + + if (intr[0] == -1) + return; + + if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) + <= 0) + panic("Interrupt but no interrupt parent!\n"); + + if (OF_searchprop(iparent, "#interrupt-cells", &icells, sizeof(icells)) + <= 0) + icells = 1; + + for (i = 0; i < nintr; i+=icells) { + resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, + dinfo->udi_ninterrupts, INTR_VEC(iparent, intr[i]), + INTR_VEC(iparent, intr[i]), 1); + + dinfo->udi_interrupts[dinfo->udi_ninterrupts] = + INTR_VEC(iparent, intr[i]); + dinfo->udi_ninterrupts++; + } +} + +static void +unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo) +{ + struct unin_chip_reg *reg; + int i, nreg; + + nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); + if (nreg == -1) + return; + + for (i = 0; i < nreg; i++) { + resource_list_add(&dinfo->udi_resources, SYS_RES_MEMORY, i, + reg[i].mr_base, + reg[i].mr_base + reg[i].mr_size, + reg[i].mr_size); + } +} + +static void +unin_enable_gmac(device_t dev) +{ volatile u_int *clkreg; + struct unin_chip_softc *sc; u_int32_t tmpl; - if (uncsc == NULL) - panic("unin_enable_gmac: device not found"); - - clkreg = (void *)(uncsc->sc_addr + UNIN_CLOCKCNTL); + sc = device_get_softc(dev); + clkreg = (void *)(sc->sc_addr + UNIN_CLOCKCNTL); tmpl = inl(clkreg); tmpl |= UNIN_CLOCKCNTL_GMAC; outl(clkreg, tmpl); } +static void +unin_enable_mpic(device_t dev) +{ + volatile u_int *toggle; + struct unin_chip_softc *sc; + u_int32_t tmpl; + + sc = device_get_softc(dev); + toggle = (void *)(sc->sc_addr + UNIN_TOGGLE_REG); + tmpl = inl(toggle); + tmpl |= UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE; + outl(toggle, tmpl); +} + static int unin_chip_probe(device_t dev) { @@ -611,7 +749,8 @@ if (name == NULL) return (ENXIO); - if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0) + if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0 + && strcmp(name, "u4") != 0) return (ENXIO); device_set_desc(dev, "Apple UniNorth System Controller"); @@ -621,53 +760,339 @@ static int unin_chip_attach(device_t dev) { - phandle_t node; + struct unin_chip_softc *sc; + struct unin_chip_devinfo *dinfo; + phandle_t root; + phandle_t child; + device_t cdev; + char compat[32]; u_int reg[3]; - int i = 0; + int error, i = 0; - uncsc = device_get_softc(dev); - node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + root = ofw_bus_get_node(dev); - if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) + if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) return (ENXIO); - if (strcmp(ofw_bus_get_name(dev), "u3") == 0) + if (strcmp(ofw_bus_get_name(dev), "u3") == 0 + || strcmp(ofw_bus_get_name(dev), "u4") == 0) i = 1; /* #address-cells lies */ - uncsc->sc_physaddr = reg[i]; - uncsc->sc_size = reg[i+1]; + sc->sc_physaddr = reg[i]; + sc->sc_size = reg[i+1]; + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "UniNorth Device Memory"; + + error = rman_init(&sc->sc_mem_rman); + + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + return (error); + } + + error = rman_manage_region(&sc->sc_mem_rman, sc->sc_physaddr, + sc->sc_physaddr + sc->sc_size - 1); + if (error) { + device_printf(dev, + "rman_manage_region() failed. error = %d\n", + error); + return (error); + } + + /* + * Iterate through the sub-devices + */ + for (child = OF_child(root); child != 0; child = OF_peer(child)) { + dinfo = malloc(sizeof(*dinfo), M_UNIN, M_WAITOK | M_ZERO); + if (ofw_bus_gen_setup_devinfo(&dinfo->udi_obdinfo, child) + != 0) + { + free(dinfo, M_UNIN); + continue; + } + + resource_list_init(&dinfo->udi_resources); + dinfo->udi_ninterrupts = 0; + unin_chip_add_intr(child, dinfo); + + unin_chip_add_reg(child, dinfo); + + cdev = device_add_child(dev, NULL, -1); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + dinfo->udi_obdinfo.obd_name); + resource_list_free(&dinfo->udi_resources); + ofw_bus_gen_destroy_devinfo(&dinfo->udi_obdinfo); + free(dinfo, M_UNIN); + continue; + } + + device_set_ivars(cdev, dinfo); + } + /* * Only map the first page, since that is where the registers * of interest lie. */ - uncsc->sc_addr = (vm_offset_t) pmap_mapdev(uncsc->sc_physaddr, - PAGE_SIZE); + sc->sc_addr = (vm_offset_t)pmap_mapdev(sc->sc_physaddr, PAGE_SIZE); - uncsc->sc_version = *(u_int *)uncsc->sc_addr; - device_printf(dev, "Version %d\n", uncsc->sc_version); + sc->sc_version = *(u_int *)sc->sc_addr; + device_printf(dev, "Version %d\n", sc->sc_version); - return (0); + /* + * Enable the GMAC Ethernet cell and the integrated OpenPIC + * if Open Firmware says they are used. + */ + for (child = OF_child(root); child; child = OF_peer(child)) { + memset(compat, 0, sizeof(compat)); + OF_getprop(child, "compatible", compat, sizeof(compat)); + if (strcmp(compat, "gmac") == 0) + unin_enable_gmac(dev); + if (strcmp(compat, "chrp,open-pic") == 0) + unin_enable_mpic(dev); + } + + /* + * GMAC lives under the PCI bus, so just check if enet is gmac. + */ + child = OF_finddevice("enet"); + memset(compat, 0, sizeof(compat)); + OF_getprop(child, "compatible", compat, sizeof(compat)); + if (strcmp(compat, "gmac") == 0) + unin_enable_gmac(dev); + + return (bus_generic_attach(dev)); } -static device_method_t unin_chip_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, unin_chip_probe), - DEVMETHOD(device_attach, unin_chip_attach), +static int +unin_chip_print_child(device_t dev, device_t child) +{ + struct unin_chip_devinfo *dinfo; + struct resource_list *rl; + int retval = 0; - { 0, 0 } -}; + dinfo = device_get_ivars(child); + rl = &dinfo->udi_resources; -static driver_t unin_chip_driver = { - "unin", - unin_chip_methods, - sizeof(struct unin_chip_softc) -}; + retval += bus_print_child_header(dev, child); -static devclass_t unin_chip_devclass; + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); -DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0); + retval += bus_print_child_footer(dev, child); + return (retval); +} +static void +unin_chip_probe_nomatch(device_t dev, device_t child) +{ + struct unin_chip_devinfo *dinfo; + struct resource_list *rl; + const char *type; + if (bootverbose) { + dinfo = device_get_ivars(child); + rl = &dinfo->udi_resources; + if ((type = ofw_bus_get_type(child)) == NULL) + type = "(unknown)"; + device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); + resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + printf(" (no driver attached)\n"); + } +} + + +static struct resource * +unin_chip_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 unin_chip_softc *sc; + int needactivate; + struct resource *rv; + struct rman *rm; + u_long adjstart, adjend, adjcount; + struct unin_chip_devinfo *dinfo; + struct resource_list_entry *rle; + + sc = device_get_softc(bus); + dinfo = device_get_ivars(child); + + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + + switch (type) { + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + rle = resource_list_find(&dinfo->udi_resources, SYS_RES_MEMORY, + *rid); + if (rle == NULL) { + device_printf(bus, "no rle for %s memory %d\n", + device_get_nameunit(child), *rid); + return (NULL); + } + + rle->end = rle->end - 1; /* Hack? */ + + if (start < rle->start) + adjstart = rle->start; + else if (start > rle->end) + adjstart = rle->end; + else + adjstart = start; + + if (end < rle->start) + adjend = rle->start; + else if (end > rle->end) + adjend = rle->end; + else + adjend = end; + + adjcount = adjend - adjstart; + + rm = &sc->sc_mem_rman; + break; + + case SYS_RES_IRQ: + /* Check for passthrough from subattachments. */ + if (device_get_parent(child) != bus) + return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, + type, rid, start, end, count, + flags); + + rle = resource_list_find(&dinfo->udi_resources, SYS_RES_IRQ, + *rid); + if (rle == NULL) { + if (dinfo->udi_ninterrupts >= 6) { + device_printf(bus, + "%s has more than 6 interrupts\n", + device_get_nameunit(child)); + return (NULL); + } + resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, + dinfo->udi_ninterrupts, start, start, + 1); + + dinfo->udi_interrupts[dinfo->udi_ninterrupts] = start; + dinfo->udi_ninterrupts++; + } + + return (resource_list_alloc(&dinfo->udi_resources, bus, child, + type, rid, start, end, count, + flags)); + default: + device_printf(bus, "unknown resource request from %s\n", + device_get_nameunit(child)); + return (NULL); + } + + rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, + child); + if (rv == NULL) { + device_printf(bus, + "failed to reserve resource %#lx - %#lx (%#lx)" + " for %s\n", adjstart, adjend, adjcount, + device_get_nameunit(child)); + return (NULL); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv) != 0) { + device_printf(bus, + "failed to activate resource for %s\n", + device_get_nameunit(child)); + rman_release_resource(rv); + return (NULL); + } + } + + return (rv); +} + +static int +unin_chip_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + if (rman_get_flags(res) & RF_ACTIVE) { + int error = bus_deactivate_resource(child, type, rid, res); + if (error) + return error; + } + + return (rman_release_resource(res)); +} + +static int +unin_chip_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + struct unin_chip_softc *sc; + void *p; + + sc = device_get_softc(bus); + + if (type == SYS_RES_IRQ) + return (bus_activate_resource(bus, type, rid, res)); + + if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { + vm_offset_t start; + + start = (vm_offset_t) rman_get_start(res); + + if (bootverbose) + printf("unin mapdev: start %zx, len %ld\n", start, + rman_get_size(res)); + + p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); + if (p == NULL) + return (ENOMEM); + rman_set_virtual(res, p); + rman_set_bustag(res, &bs_be_tag); + rman_set_bushandle(res, (u_long)p); + } + + return (rman_activate_resource(res)); +} + + +static int +unin_chip_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + /* + * If this is a memory resource, unmap it. + */ + if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { + u_int32_t psize; + + psize = rman_get_size(res); + pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); + } + + return (rman_deactivate_resource(res)); +} + + +static struct resource_list * +unin_chip_get_resource_list (device_t dev, device_t child) +{ + struct unin_chip_devinfo *dinfo; + + dinfo = device_get_ivars(child); + return (&dinfo->udi_resources); +} + +static const struct ofw_bus_devinfo * +unin_chip_get_devinfo(device_t dev, device_t child) +{ + struct unin_chip_devinfo *dinfo; + + dinfo = device_get_ivars(child); + return (&dinfo->udi_obdinfo); +} Index: powerpc/powermac/grackle.c =================================================================== --- powerpc/powermac/grackle.c (revision 206272) +++ powerpc/powermac/grackle.c (working copy) @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -341,13 +342,15 @@ struct grackle_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr; + phandle_t iparent; uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; sc = device_get_softc(bus); pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, - sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf)) - return (mintr); + sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), + &iparent, maskbuf)) + return (INTR_VEC(iparent, mintr)); /* Maybe it's a real interrupt, not an intpin */ if (pin > 4) Index: powerpc/powermac/hrowpic.c =================================================================== --- powerpc/powermac/hrowpic.c (revision 206272) +++ powerpc/powermac/hrowpic.c (working copy) @@ -70,6 +70,7 @@ static void hrowpic_ipi(device_t, u_int); static void hrowpic_mask(device_t, u_int); static void hrowpic_unmask(device_t, u_int); +static uint32_t hrowpic_id(device_t dev); static device_method_t hrowpic_methods[] = { /* Device interface */ @@ -80,6 +81,7 @@ DEVMETHOD(pic_dispatch, hrowpic_dispatch), DEVMETHOD(pic_enable, hrowpic_enable), DEVMETHOD(pic_eoi, hrowpic_eoi), + DEVMETHOD(pic_id, hrowpic_id), DEVMETHOD(pic_ipi, hrowpic_ipi), DEVMETHOD(pic_mask, hrowpic_mask), DEVMETHOD(pic_unmask, hrowpic_unmask), @@ -169,6 +171,8 @@ hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); powerpc_register_pic(dev, 64); + root_pic = dev; /* Heathrow systems have only one PIC */ + return (0); } @@ -282,3 +286,10 @@ sc = device_get_softc(dev); hrowpic_toggle_irq(sc, irq, 1); } + +static uint32_t +hrowpic_id(device_t dev) +{ + return (ofw_bus_get_node(dev)); +} + Index: powerpc/powermac/kiic.c =================================================================== --- powerpc/powermac/kiic.c (revision 206272) +++ powerpc/powermac/kiic.c (working copy) @@ -144,6 +144,7 @@ static devclass_t kiic_devclass; DRIVER_MODULE(kiic, macio, kiic_driver, kiic_devclass, 0, 0); +DRIVER_MODULE(kiic, unin, kiic_driver, kiic_devclass, 0, 0); static int kiic_probe(device_t self) Index: powerpc/powermac/macgpio.c =================================================================== --- powerpc/powermac/macgpio.c (revision 206272) +++ powerpc/powermac/macgpio.c (working copy) @@ -35,15 +35,16 @@ #include #include #include -#include #include -#include #include #include + +#include +#include #include - #include +#include #include #include @@ -148,8 +149,7 @@ { struct macgpio_softc *sc; struct macgpio_devinfo *dinfo; - phandle_t root; - phandle_t child; + phandle_t root, child, iparent; device_t cdev; uint32_t irq; @@ -184,10 +184,13 @@ resource_list_init(&dinfo->mdi_resources); - if (OF_getprop(child,"interrupts",&irq, sizeof(irq)) == + if (OF_getprop(child, "interrupts", &irq, sizeof(irq)) == sizeof(irq)) { + OF_searchprop(child, "interrupt-parent", &iparent, + sizeof(iparent)); resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, - 0, irq, irq, 1); + 0, INTR_VEC(iparent, irq), INTR_VEC(iparent, irq), + 1); } /* Fix messed-up offsets */ Index: powerpc/powermac/uninorthvar.h =================================================================== --- powerpc/powermac/uninorthvar.h (revision 206272) +++ powerpc/powermac/uninorthvar.h (working copy) @@ -67,13 +67,33 @@ }; struct unin_chip_softc { - vm_offset_t sc_physaddr; + u_int32_t sc_physaddr; vm_offset_t sc_addr; - u_int sc_size; + u_int32_t sc_size; + struct rman sc_mem_rman; int sc_version; }; /* + * Format of a unin reg property entry. + */ +struct unin_chip_reg { + u_int32_t mr_base; + u_int32_t mr_size; +}; + +/* + * Per unin device structure. + */ +struct unin_chip_devinfo { + int udi_interrupts[6]; + int udi_ninterrupts; + int udi_base; + struct ofw_bus_devinfo udi_obdinfo; + struct resource_list udi_resources; +}; + +/* * Version register */ #define UNIN_VERS 0x0 @@ -81,7 +101,14 @@ /* * Clock-control register */ -#define UNIN_CLOCKCNTL 0x20 -#define UNIN_CLOCKCNTL_GMAC 0x2 +#define UNIN_CLOCKCNTL 0x20 +#define UNIN_CLOCKCNTL_GMAC 0x2 +/* + * Toggle registers + */ +#define UNIN_TOGGLE_REG 0xe0 +#define UNIN_MPIC_RESET 0x2 +#define UNIN_MPIC_OUTPUT_ENABLE 0x4 + #endif /* _POWERPC_POWERMAC_UNINORTHVAR_H_ */ Index: powerpc/powermac/macio.c =================================================================== --- powerpc/powermac/macio.c (revision 206272) +++ powerpc/powermac/macio.c (working copy) @@ -37,15 +37,16 @@ #include #include #include -#include #include -#include #include #include + +#include +#include #include - #include +#include #include #include @@ -186,6 +187,7 @@ static void macio_add_intr(phandle_t devnode, struct macio_devinfo *dinfo) { + phandle_t iparent; int *intr; int i, nintr; int icells; @@ -211,11 +213,17 @@ if (intr[0] == -1) return; + if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) + <= 0) + panic("Interrupt but no interrupt parent!\n"); + for (i = 0; i < nintr; i+=icells) { resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, - dinfo->mdi_ninterrupts, intr[i], intr[i], 1); + dinfo->mdi_ninterrupts, INTR_VEC(iparent, intr[i]), + INTR_VEC(iparent, intr[i]), 1); - dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = intr[i]; + dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = + INTR_VEC(iparent, intr[i]); dinfo->mdi_ninterrupts++; } } Index: dev/ofw/ofw_bus_subr.c =================================================================== --- dev/ofw/ofw_bus_subr.c (revision 206272) +++ dev/ofw/ofw_bus_subr.c (working copy) @@ -174,7 +174,7 @@ int ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, - void *maskbuf) + phandle_t *iparent, void *maskbuf) { int rv; @@ -188,7 +188,7 @@ panic("ofw_bus_lookup_imap: could not get reg property"); return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, - mintrsz)); + mintrsz, iparent)); } /* @@ -211,7 +211,7 @@ int ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, - int rintrsz) + int rintrsz, phandle_t *iparent) { phandle_t parent; uint8_t *ref = maskbuf; @@ -255,6 +255,9 @@ if (bcmp(ref, mptr, physsz + intrsz) == 0) { bcopy(mptr + physsz + intrsz + sizeof(parent), result, rintrsz); + + if (iparent != NULL) + *iparent = parent; return (1); } mptr += tsz; Index: dev/ofw/ofw_bus_subr.h =================================================================== --- dev/ofw/ofw_bus_subr.h (revision 206272) +++ dev/ofw/ofw_bus_subr.h (working copy) @@ -63,8 +63,11 @@ /* Routines for processing firmware interrupt maps */ void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, - void *, int, void *, int, void *); + void *, int, void *, int, phandle_t *, void *); int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, - void *, void *, int); + void *, void *, int, phandle_t *); +/* Helper to get node's interrupt parent */ +void ofw_bus_find_iparent(phandle_t); + #endif /* !_DEV_OFW_OFW_BUS_SUBR_H_ */ Index: sparc64/pci/schizo.c =================================================================== --- sparc64/pci/schizo.c (revision 206273) +++ sparc64/pci/schizo.c (working copy) @@ -1039,7 +1039,7 @@ pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), - maskbuf)) + NULL, maskbuf)) return (mintr); device_printf(bridge, "could not route pin %d for device %d.%d\n", Index: sparc64/pci/psycho.c =================================================================== --- sparc64/pci/psycho.c (revision 206273) +++ sparc64/pci/psycho.c (working copy) @@ -1046,7 +1046,7 @@ pintr = pin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), - maskbuf)) + NULL, maskbuf)) return (mintr); /* * If this is outside of the range for an intpin, it's likely a full Index: sparc64/pci/ofw_pcib_subr.c =================================================================== --- sparc64/pci/ofw_pcib_subr.c (revision 206272) +++ sparc64/pci/ofw_pcib_subr.c (working copy) @@ -77,7 +77,7 @@ pintr = intpin; if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), - maskbuf)) { + NULL, maskbuf)) { /* * If we've found a mapping, return it and don't map * it again on higher levels - that causes problems Index: sparc64/isa/ofw_isa.c =================================================================== --- sparc64/isa/ofw_isa.c (revision 206272) +++ sparc64/isa/ofw_isa.c (working copy) @@ -113,7 +113,7 @@ * fully specified, so we may not continue to map. */ if (!ofw_bus_lookup_imap(node, ii, ®, sizeof(reg), - &intr, sizeof(intr), &mintr, sizeof(mintr), maskbuf)) { + &intr, sizeof(intr), &mintr, sizeof(mintr), NULL, maskbuf)) { /* Try routing at the parent bridge. */ mintr = PCIB_ROUTE_INTERRUPT(pbridge, bridge, intr); }