diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-05-16 15:18:25 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-05-16 15:18:25 +0000 |
commit | 95e9f935c778d480ae3972c330bd79608917f78e (patch) | |
tree | 96903f191716aa69b72b0de38716686fcc5210d7 | |
parent | 0e169b2cc79c453acd04e46d2d7adbb230f9920a (diff) | |
download | FreeBSD-src-95e9f935c778d480ae3972c330bd79608917f78e.zip FreeBSD-src-95e9f935c778d480ae3972c330bd79608917f78e.tar.gz |
Add support for the U4 PCI-Express bridge chipset used in late-generation
Powermac G5 systems. MSI and several other things are not presently
supported.
The U3/U4 internal device support portions of this change were contributed
by Andreas Tobler.
MFC after: 1 week
-rw-r--r-- | sys/conf/files.powerpc | 3 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 6 | ||||
-rw-r--r-- | sys/powerpc/include/intr_machdep.h | 5 | ||||
-rw-r--r-- | sys/powerpc/powermac/cpcht.c | 851 | ||||
-rw-r--r-- | sys/powerpc/powermac/cpchtvar.h | 58 | ||||
-rw-r--r-- | sys/powerpc/powermac/uninorth.c | 842 | ||||
-rw-r--r-- | sys/powerpc/powermac/uninorthpci.c | 543 | ||||
-rw-r--r-- | sys/powerpc/powermac/uninorthvar.h | 37 | ||||
-rw-r--r-- | sys/powerpc/powerpc/openpic.c | 10 |
9 files changed, 1491 insertions, 864 deletions
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index c263ab5..0fbed82 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -139,7 +139,8 @@ powerpc/powermac/openpic_macio.c optional powermac pci powerpc/powermac/pswitch.c optional powermac pswitch powerpc/powermac/pmu.c optional powermac pmu powerpc/powermac/smu.c optional powermac smu -powerpc/powermac/uninorth.c optional powermac pci +powerpc/powermac/uninorth.c optional powermac +powerpc/powermac/uninorthpci.c optional powermac pci powerpc/powermac/vcoregpio.c optional powermac powerpc/powerpc/altivec.c optional aim powerpc/powerpc/atomic.S standard diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 29593eb1..f4b7afe 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -53,7 +53,7 @@ __FBSDID("$FreeBSD$"); #include <machine/resource.h> #include <machine/stdarg.h> -#if defined(__i386__) || defined(__amd64__) +#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) #include <machine/intr_machdep.h> #endif @@ -559,7 +559,7 @@ pci_read_extcap(device_t pcib, pcicfgregs *cfg) { #define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w) #define WREG(n, v, w) PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w) -#if defined(__i386__) || defined(__amd64__) +#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) uint64_t addr; #endif uint32_t val; @@ -603,7 +603,7 @@ pci_read_extcap(device_t pcib, pcicfgregs *cfg) cfg->pp.pp_data = ptr + PCIR_POWER_DATA; } break; -#if defined(__i386__) || defined(__amd64__) +#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) case PCIY_HT: /* HyperTransport */ /* Determine HT-specific capability type. */ val = REG(ptr + PCIR_HT_COMMAND, 2); diff --git a/sys/powerpc/include/intr_machdep.h b/sys/powerpc/include/intr_machdep.h index f25d937..5f0a02a 100644 --- a/sys/powerpc/include/intr_machdep.h +++ b/sys/powerpc/include/intr_machdep.h @@ -30,6 +30,11 @@ #define INTR_VECTORS 256 +/* + * Default base address for MSI messages on PowerPC + */ +#define MSI_INTEL_ADDR_BASE 0xfee00000 + extern device_t pic; extern device_t pic8259; diff --git a/sys/powerpc/powermac/cpcht.c b/sys/powerpc/powermac/cpcht.c index b8ab22f..a0c5eb2 100644 --- a/sys/powerpc/powermac/cpcht.c +++ b/sys/powerpc/powermac/cpcht.c @@ -1,5 +1,5 @@ /*- - * Copyright (C) 2008 Nathan Whitehorn + * Copyright (C) 2008-2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,7 +39,9 @@ #include <dev/pci/pcireg.h> #include <machine/bus.h> +#include <machine/intr_machdep.h> #include <machine/md_var.h> +#include <machine/openpicvar.h> #include <machine/pio.h> #include <machine/resource.h> @@ -47,396 +49,341 @@ #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include <powerpc/powermac/cpchtvar.h> #include <vm/vm.h> #include <vm/pmap.h> #include "pcib_if.h" - -#include "opt_isa.h" - -#ifdef DEV_ISA -#include <isa/isavar.h> -#endif - -static MALLOC_DEFINE(M_CPCHT, "cpcht", "CPC HT device information"); +#include "pic_if.h" /* - * HT Driver methods. + * IBM CPC9X5 Hypertransport Device interface. */ static int cpcht_probe(device_t); static int cpcht_attach(device_t); -static ofw_bus_get_devinfo_t cpcht_get_devinfo; - - -static device_method_t cpcht_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, cpcht_probe), - DEVMETHOD(device_attach, cpcht_attach), - - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), - DEVMETHOD(bus_release_resource, bus_generic_release_resource), - DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), - - /* ofw_bus interface */ - DEVMETHOD(ofw_bus_get_devinfo, cpcht_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 cpcht_driver = { - "cpcht", - cpcht_methods, - 0 -}; - -static devclass_t cpcht_devclass; - -DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0); - -static int -cpcht_probe(device_t dev) -{ - const char *type, *compatible; - - type = ofw_bus_get_type(dev); - compatible = ofw_bus_get_compat(dev); - - if (type == NULL || compatible == NULL) - return (ENXIO); - - if (strcmp(type, "ht") != 0) - return (ENXIO); - - if (strcmp(compatible, "u3-ht") == 0) { - device_set_desc(dev, "IBM CPC925 HyperTransport Tunnel"); - return (0); - } else if (strcmp(compatible,"u4-ht") == 0) { - device_set_desc(dev, "IBM CPC945 HyperTransport Tunnel"); - return (0); - } - - return (ENXIO); -} - -static int -cpcht_attach(device_t dev) -{ - phandle_t root, child; - device_t cdev; - struct ofw_bus_devinfo *dinfo; - u_int32_t reg[6]; - - root = ofw_bus_get_node(dev); - - if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) - return (ENXIO); - - for (child = OF_child(root); child != 0; child = OF_peer(child)) { - dinfo = malloc(sizeof(*dinfo), M_CPCHT, M_WAITOK | M_ZERO); - - if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { - free(dinfo, M_CPCHT); - continue; - } - cdev = device_add_child(dev, NULL, -1); - if (cdev == NULL) { - device_printf(dev, "<%s>: device_add_child failed\n", - dinfo->obd_name); - ofw_bus_gen_destroy_devinfo(dinfo); - free(dinfo, M_CPCHT); - continue; - } - device_set_ivars(cdev, dinfo); - } - - return (bus_generic_attach(dev)); -} - -static const struct ofw_bus_devinfo * -cpcht_get_devinfo(device_t dev, device_t child) -{ - return (device_get_ivars(child)); -} - -#ifdef DEV_ISA - -/* - * CPC ISA Device interface. - */ -static int cpcisa_probe(device_t); - -/* - * Driver methods. - */ -static device_method_t cpcisa_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, cpcisa_probe), - DEVMETHOD(device_attach, isab_attach), - /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), - DEVMETHOD(bus_release_resource, bus_generic_release_resource), - DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), - - {0,0} -}; - -static driver_t cpcisa_driver = { - "isab", - cpcisa_methods, - 0 -}; - -DRIVER_MODULE(cpcisa, cpcht, cpcisa_driver, isab_devclass, 0, 0); - -static int -cpcisa_probe(device_t dev) -{ - const char *type; - - type = ofw_bus_get_type(dev); - - if (type == NULL) - return (ENXIO); - - if (strcmp(type, "isa") != 0) - return (ENXIO); - - device_set_desc(dev, "HyperTransport-ISA bridge"); - - return (0); -} - -#endif /* DEV_ISA */ - -/* - * CPC PCI Device interface. - */ -static int cpcpci_probe(device_t); -static int cpcpci_attach(device_t); +static void cpcht_configure_htbridge(device_t, phandle_t); /* * Bus interface. */ -static int cpcpci_read_ivar(device_t, device_t, int, +static int cpcht_read_ivar(device_t, device_t, int, uintptr_t *); -static struct resource * cpcpci_alloc_resource(device_t bus, - device_t child, int type, int *rid, u_long start, - u_long end, u_long count, u_int flags); -static int cpcpci_activate_resource(device_t bus, device_t child, +static struct resource *cpcht_alloc_resource(device_t bus, device_t child, + int type, int *rid, u_long start, u_long end, + u_long count, u_int flags); +static int cpcht_activate_resource(device_t bus, device_t child, + int type, int rid, struct resource *res); +static int cpcht_release_resource(device_t bus, device_t child, + int type, int rid, struct resource *res); +static int cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res); /* * pcib interface. */ -static int cpcpci_maxslots(device_t); -static u_int32_t cpcpci_read_config(device_t, u_int, u_int, u_int, +static int cpcht_maxslots(device_t); +static u_int32_t cpcht_read_config(device_t, u_int, u_int, u_int, u_int, int); -static void cpcpci_write_config(device_t, u_int, u_int, u_int, +static void cpcht_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); -static int cpcpci_route_interrupt(device_t, device_t, int); +static int cpcht_route_interrupt(device_t bus, device_t dev, + int pin); /* * ofw_bus interface */ -static phandle_t cpcpci_get_node(device_t bus, device_t child); +static phandle_t cpcht_get_node(device_t bus, device_t child); /* * Driver methods. */ -static device_method_t cpcpci_methods[] = { +static device_method_t cpcht_methods[] = { /* Device interface */ - DEVMETHOD(device_probe, cpcpci_probe), - DEVMETHOD(device_attach, cpcpci_attach), + DEVMETHOD(device_probe, cpcht_probe), + DEVMETHOD(device_attach, cpcht_attach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_read_ivar, cpcpci_read_ivar), + DEVMETHOD(bus_read_ivar, cpcht_read_ivar), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - DEVMETHOD(bus_alloc_resource, cpcpci_alloc_resource), - DEVMETHOD(bus_activate_resource, cpcpci_activate_resource), + DEVMETHOD(bus_alloc_resource, cpcht_alloc_resource), + DEVMETHOD(bus_release_resource, cpcht_release_resource), + DEVMETHOD(bus_activate_resource, cpcht_activate_resource), + DEVMETHOD(bus_deactivate_resource, cpcht_deactivate_resource), /* pcib interface */ - DEVMETHOD(pcib_maxslots, cpcpci_maxslots), - DEVMETHOD(pcib_read_config, cpcpci_read_config), - DEVMETHOD(pcib_write_config, cpcpci_write_config), - DEVMETHOD(pcib_route_interrupt, cpcpci_route_interrupt), + DEVMETHOD(pcib_maxslots, cpcht_maxslots), + DEVMETHOD(pcib_read_config, cpcht_read_config), + DEVMETHOD(pcib_write_config, cpcht_write_config), + DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), /* ofw_bus interface */ - DEVMETHOD(ofw_bus_get_node, cpcpci_get_node), + DEVMETHOD(ofw_bus_get_node, cpcht_get_node), { 0, 0 } }; -static driver_t cpcpci_driver = { +struct cpcht_irq { + int ht_source; + + vm_offset_t ht_base; + vm_offset_t apple_eoi; + uint32_t eoi_data; + int edge; +}; + +static struct cpcht_irq *cpcht_irqmap = NULL; + +struct cpcht_softc { + device_t sc_dev; + phandle_t sc_node; + vm_offset_t sc_data; + uint64_t sc_populated_slots; + struct rman sc_mem_rman; + + struct cpcht_irq htirq_map[128]; +}; + +static driver_t cpcht_driver = { "pcib", - cpcpci_methods, - sizeof(struct cpcpci_softc) + cpcht_methods, + sizeof(struct cpcht_softc) }; -static devclass_t cpcpci_devclass; +static devclass_t cpcht_devclass; -DRIVER_MODULE(cpcpci, cpcht, cpcpci_driver, cpcpci_devclass, 0, 0); +DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0); + +#define HTAPIC_REQUEST_EOI 0x20 +#define HTAPIC_TRIGGER_LEVEL 0x02 +#define HTAPIC_MASK 0x01 + +struct cpcht_range { + u_int32_t pci_hi; + u_int32_t pci_mid; + u_int32_t pci_lo; + u_int32_t junk; + u_int32_t host_hi; + u_int32_t host_lo; + u_int32_t size_hi; + u_int32_t size_lo; +}; static int -cpcpci_probe(device_t dev) +cpcht_probe(device_t dev) { - const char *type; + const char *type, *compatible; type = ofw_bus_get_type(dev); + compatible = ofw_bus_get_compat(dev); + + if (type == NULL || compatible == NULL) + return (ENXIO); - if (type == NULL) + if (strcmp(type, "ht") != 0) return (ENXIO); - if (strcmp(type, "pci") != 0) + if (strcmp(compatible, "u3-ht") != 0) return (ENXIO); - device_set_desc(dev, "HyperTransport-PCI bridge"); - + + device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel"); return (0); } static int -cpcpci_attach(device_t dev) +cpcht_attach(device_t dev) { - struct cpcpci_softc *sc; - phandle_t node; - u_int32_t reg[2], busrange[2], config_base; - struct cpcpci_range *rp, *io, *mem[2]; - struct cpcpci_range fakeio; - int nmem, i; + struct cpcht_softc *sc; + phandle_t node, child; + u_int32_t reg[3]; + int error; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); - if (OF_getprop(OF_parent(node), "reg", reg, sizeof(reg)) < 8) - return (ENXIO); - - if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12) return (ENXIO); sc->sc_dev = dev; sc->sc_node = node; - sc->sc_bus = busrange[0]; - config_base = reg[1]; - if (sc->sc_bus) - config_base += 0x01000000UL + (sc->sc_bus << 16); - sc->sc_data = (vm_offset_t)pmap_mapdev(config_base, PAGE_SIZE << 4); - - bzero(sc->sc_range, sizeof(sc->sc_range)); - sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, - sizeof(sc->sc_range)); - - if (sc->sc_nrange == -1) { - device_printf(dev, "could not get ranges\n"); - return (ENXIO); + sc->sc_populated_slots = 0; + sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); + + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "CPCHT Device Memory"; + error = rman_init(&sc->sc_mem_rman); + + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + return (error); } - sc->sc_range[6].pci_hi = 0; - io = NULL; - nmem = 0; + /* + * Set up the resource manager and the HT->MPIC mapping. For cpcht, + * the ranges are properties of the child bridges, and this is also + * where we get the HT interrupts properties. + */ + + bzero(sc->htirq_map, sizeof(sc->htirq_map)); + for (child = OF_child(node); child != 0; child = OF_peer(child)) + cpcht_configure_htbridge(dev, child); + + /* Now make the mapping table available to the MPIC */ + cpcht_irqmap = sc->htirq_map; + + device_add_child(dev, "pci", device_get_unit(dev)); + + return (bus_generic_attach(dev)); +} + +static void +cpcht_configure_htbridge(device_t dev, phandle_t child) +{ + struct cpcht_softc *sc; + struct ofw_pci_register pcir; + struct cpcht_range ranges[6], *rp; + int nranges, ptr, nextptr; + uint32_t vend, val; + int i, nirq, irq; + u_int f, s; - for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { + sc = device_get_softc(dev); + if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) + return; + + s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); + f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); + + /* + * Mark this slot is populated. The remote south bridge does + * not like us talking to unpopulated slots on the root bus. + */ + sc->sc_populated_slots |= (1 << s); + + /* + * Next grab this child bus's bus ranges. + */ + bzero(ranges, sizeof(ranges)); + nranges = OF_getprop(child, "ranges", ranges, sizeof(ranges)); + + ranges[6].pci_hi = 0; + for (rp = ranges; rp->pci_hi != 0; rp++) { switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: break; case OFW_PCI_PHYS_HI_SPACE_IO: - io = rp; - break; case OFW_PCI_PHYS_HI_SPACE_MEM32: - mem[nmem] = rp; - nmem++; + rman_manage_region(&sc->sc_mem_rman, rp->pci_lo, + rp->pci_lo + rp->size_lo - 1); break; case OFW_PCI_PHYS_HI_SPACE_MEM64: + panic("64-bit CPCHT reserved memory!"); break; } } - if (io == NULL) { - /* - * On at least some machines, the I/O port range is - * not exported in the OF device tree. So hardcode it. - */ - - fakeio.host_lo = 0; - fakeio.pci_lo = reg[1]; - fakeio.size_lo = 0x00400000; - if (sc->sc_bus) - fakeio.pci_lo += 0x02000000UL + (sc->sc_bus << 14); - io = &fakeio; - } - sc->sc_io_rman.rm_type = RMAN_ARRAY; - sc->sc_io_rman.rm_descr = "CPC 9xx PCI I/O Ports"; - sc->sc_iostart = io->host_lo; - if (rman_init(&sc->sc_io_rman) != 0 || - rman_manage_region(&sc->sc_io_rman, io->pci_lo, - io->pci_lo + io->size_lo - 1) != 0) { - device_printf(dev, "failed to set up io range management\n"); - return (ENXIO); - } - - if (nmem == 0) { - device_printf(dev, "can't find mem ranges\n"); - return (ENXIO); - } - sc->sc_mem_rman.rm_type = RMAN_ARRAY; - sc->sc_mem_rman.rm_descr = "CPC 9xx PCI Memory"; - if (rman_init(&sc->sc_mem_rman) != 0) { - device_printf(dev, - "failed to init mem range resources\n"); - return (ENXIO); - } - for (i = 0; i < nmem; i++) { - if (rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, - mem[i]->pci_lo + mem[i]->size_lo - 1) != 0) { - device_printf(dev, - "failed to set up memory range management\n"); - return (ENXIO); + /* + * Next build up any HT->MPIC mappings for this sub-bus. One would + * naively hope that enabling, disabling, and EOIing interrupts would + * cause the appropriate HT bus transactions to that effect. This is + * not the case. + * + * Instead, we have to muck about on the HT peer's root PCI bridges, + * figure out what interrupts they send, enable them, and cache + * the location of their WaitForEOI registers so that we can + * send EOIs later. + */ + + /* All the devices we are interested in have caps */ + if (!(PCIB_READ_CONFIG(dev, 0, s, f, PCIR_STATUS, 2) + & PCIM_STATUS_CAPPRESENT)) + return; + + nextptr = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_CAP_PTR, 1); + while (nextptr != 0) { + ptr = nextptr; + nextptr = PCIB_READ_CONFIG(dev, 0, s, f, + ptr + PCICAP_NEXTPTR, 1); + + /* Find the HT IRQ capabilities */ + if (PCIB_READ_CONFIG(dev, 0, s, f, + ptr + PCICAP_ID, 1) != PCIY_HT) + continue; + + val = PCIB_READ_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 2); + if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT) + continue; + + /* Ask for the IRQ count */ + PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); + nirq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); + nirq = ((nirq >> 16) & 0xff) + 1; + + device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); + + for (i = 0; i < nirq; i++) { + PCIB_WRITE_CONFIG(dev, 0, s, f, + ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); + irq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); + + /* + * Mask this interrupt for now. + */ + PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + 4, + irq | HTAPIC_MASK, 4); + irq = (irq >> 16) & 0xff; + + sc->htirq_map[irq].ht_source = i; + sc->htirq_map[irq].ht_base = sc->sc_data + + (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); + + PCIB_WRITE_CONFIG(dev, 0, s, f, + ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); + sc->htirq_map[irq].eoi_data = + PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4) | + 0x80000000; + + /* + * Apple uses a non-compliant IO/APIC that differs + * in how we signal EOIs. Check if this device was + * made by Apple, and act accordingly. + */ + vend = PCIB_READ_CONFIG(dev, 0, s, f, + PCIR_DEVVENDOR, 4); + if ((vend & 0xffff) == 0x106b) + sc->htirq_map[irq].apple_eoi = + (sc->htirq_map[irq].ht_base - ptr) + 0x60; } } - - ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); - - device_add_child(dev, "pci", device_get_unit(dev)); - - return (bus_generic_attach(dev)); } static int -cpcpci_maxslots(device_t dev) +cpcht_maxslots(device_t dev) { return (PCI_SLOTMAX); } static u_int32_t -cpcpci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, +cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { - struct cpcpci_softc *sc; + struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); + if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) + return (0xffffffff); + + if (bus > 0) + caoff += 0x01000000UL + (bus << 16); + switch (width) { case 1: return (in8rb(caoff)); @@ -453,16 +400,22 @@ cpcpci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, } static void -cpcpci_write_config(device_t dev, u_int bus, u_int slot, u_int func, +cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { - struct cpcpci_softc *sc; + struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); + if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) + return; + + if (bus > 0) + caoff += 0x01000000UL + (bus << 16); + switch (width) { case 1: out8rb(caoff, val); @@ -477,9 +430,9 @@ cpcpci_write_config(device_t dev, u_int bus, u_int slot, u_int func, } static int -cpcpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +cpcht_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { - struct cpcpci_softc *sc; + struct cpcht_softc *sc; sc = device_get_softc(dev); @@ -488,38 +441,53 @@ cpcpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) *result = device_get_unit(dev); return (0); case PCIB_IVAR_BUS: - *result = sc->sc_bus; + *result = 0; /* Root bus */ return (0); } return (ENOENT); } +static phandle_t +cpcht_get_node(device_t bus, device_t dev) +{ + struct cpcht_softc *sc; + + sc = device_get_softc(bus); + /* We only have one child, the PCI bus, which needs our own node. */ + return (sc->sc_node); +} + +static int +cpcht_route_interrupt(device_t bus, device_t dev, int pin) +{ + return (pin); +} + static struct resource * -cpcpci_alloc_resource(device_t bus, device_t child, int type, int *rid, +cpcht_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 cpcpci_softc *sc; + struct cpcht_softc *sc; struct resource *rv; struct rman *rm; - int needactivate; + int needactivate, err; needactivate = flags & RF_ACTIVE; flags &= ~RF_ACTIVE; sc = device_get_softc(bus); + err = 0; switch (type) { + case SYS_RES_IOPORT: + end = min(end, start + count); + + /* FALLTHROUGH */ case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; - case SYS_RES_IOPORT: - rm = &sc->sc_io_rman; - if (rm == NULL) - return (NULL); - break; - case SYS_RES_IRQ: return (bus_alloc_resource(bus, type, rid, start, end, count, flags)); @@ -553,11 +521,11 @@ cpcpci_alloc_resource(device_t bus, device_t child, int type, int *rid, } static int -cpcpci_activate_resource(device_t bus, device_t child, int type, int rid, +cpcht_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { void *p; - struct cpcpci_softc *sc; + struct cpcht_softc *sc; sc = device_get_softc(bus); @@ -568,15 +536,9 @@ cpcpci_activate_resource(device_t bus, device_t child, int type, int rid, vm_offset_t start; start = (vm_offset_t)rman_get_start(res); - /* - * For i/o-ports, convert the start address to the - * CPC PCI i/o window - */ - if (type == SYS_RES_IOPORT) - start += sc->sc_iostart; if (bootverbose) - printf("cpcpci mapdev: start %x, len %ld\n", start, + printf("cpcht mapdev: start %zx, len %ld\n", start, rman_get_size(res)); p = pmap_mapdev(start, (vm_size_t)rman_get_size(res)); @@ -590,36 +552,259 @@ cpcpci_activate_resource(device_t bus, device_t child, int type, int rid, return (rman_activate_resource(res)); } -static phandle_t -cpcpci_get_node(device_t bus, device_t dev) +static int +cpcht_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) { - struct cpcpci_softc *sc; - sc = device_get_softc(bus); - /* We only have one child, the PCI bus, which needs our own node. */ - return (sc->sc_node); + 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 -cpcpci_route_interrupt(device_t bus, device_t dev, int pin) +cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) { - struct cpcpci_softc *sc; - struct ofw_pci_register reg; - uint32_t pintr, mintr; - 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); - - /* Maybe it's a real interrupt, not an intpin */ - if (pin > 4) - return (pin); - - device_printf(bus, "could not route pin %d for device %d.%d\n", - pin, pci_get_slot(dev), pci_get_function(dev)); - return (PCI_INVALID_IRQ); + /* + * 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)); +} + +/* + * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) + */ + +static int openpic_cpcht_probe(device_t); +static int openpic_cpcht_attach(device_t); +static void openpic_cpcht_config(device_t, u_int irq, + enum intr_trigger trig, enum intr_polarity pol); +static void openpic_cpcht_enable(device_t, u_int irq, u_int vector); +static void openpic_cpcht_unmask(device_t, u_int irq); +static void openpic_cpcht_eoi(device_t, u_int irq); + +static device_method_t openpic_cpcht_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, openpic_cpcht_probe), + DEVMETHOD(device_attach, openpic_cpcht_attach), + + /* PIC interface */ + DEVMETHOD(pic_config, openpic_cpcht_config), + DEVMETHOD(pic_dispatch, openpic_dispatch), + DEVMETHOD(pic_enable, openpic_cpcht_enable), + DEVMETHOD(pic_eoi, openpic_cpcht_eoi), + DEVMETHOD(pic_ipi, openpic_ipi), + DEVMETHOD(pic_mask, openpic_mask), + DEVMETHOD(pic_unmask, openpic_cpcht_unmask), + + { 0, 0 }, +}; + +struct openpic_cpcht_softc { + struct openpic_softc sc_openpic; + + struct mtx sc_ht_mtx; +}; + +static driver_t openpic_cpcht_driver = { + "htpic", + openpic_cpcht_methods, + sizeof(struct openpic_softc), +}; + +DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0); + +static int +openpic_cpcht_probe(device_t dev) +{ + const char *type = ofw_bus_get_type(dev); + + if (strcmp(type, "open-pic") != 0) + return (ENXIO); + + device_set_desc(dev, OPENPIC_DEVSTR); + return (0); +} + +static int +openpic_cpcht_attach(device_t dev) +{ + struct openpic_cpcht_softc *sc; + int err, irq; + + err = openpic_attach(dev); + if (err != 0) + return (err); + + /* + * The HT APIC stuff is not thread-safe, so we need a mutex to + * protect it. + */ + sc = device_get_softc(dev); + mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN); + + /* + * Interrupts 0-3 are internally sourced and are level triggered + * active low. Interrupts 4-123 are connected to a pulse generator + * and should be programmed as edge triggered low-to-high. + * + * IBM CPC945 Manual, Section 9.3. + */ + + for (irq = 0; irq < 4; irq++) + openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); + for (irq = 4; irq < 124; irq++) + openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); + + return (0); +} + +static void +openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig, + enum intr_polarity pol) +{ + struct openpic_cpcht_softc *sc; + uint32_t ht_irq; + + /* + * The interrupt settings for the MPIC are completely determined + * by the internal wiring in the northbridge. Real changes to these + * settings need to be negotiated with the remote IO-APIC on the HT + * link. + */ + + sc = device_get_softc(dev); + + if (cpcht_irqmap != NULL && irq < 128 && + cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { + mtx_lock_spin(&sc->sc_ht_mtx); + + /* Program the data port */ + out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, + 0x10 + (cpcht_irqmap[irq].ht_source << 1)); + + /* Grab the IRQ config register */ + ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); + + /* Mask the IRQ while we fiddle settings */ + out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK); + + /* Program the interrupt sense */ + ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI); + if (trig == INTR_TRIGGER_EDGE) { + cpcht_irqmap[irq].edge = 1; + } else { + cpcht_irqmap[irq].edge = 0; + ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI; + } + out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); + + mtx_unlock_spin(&sc->sc_ht_mtx); + } +} + +static void +openpic_cpcht_enable(device_t dev, u_int irq, u_int vec) +{ + struct openpic_cpcht_softc *sc; + uint32_t ht_irq; + + openpic_enable(dev, irq, vec); + + sc = device_get_softc(dev); + + if (cpcht_irqmap != NULL && irq < 128 && + cpcht_irqmap[irq].ht_base > 0) { + mtx_lock_spin(&sc->sc_ht_mtx); + + /* Program the data port */ + out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, + 0x10 + (cpcht_irqmap[irq].ht_source << 1)); + + /* Unmask the interrupt */ + ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); + ht_irq &= ~HTAPIC_MASK; + out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); + + mtx_unlock_spin(&sc->sc_ht_mtx); + } + + openpic_cpcht_eoi(dev, irq); +} + +static void +openpic_cpcht_unmask(device_t dev, u_int irq) +{ + struct openpic_cpcht_softc *sc; + uint32_t ht_irq; + + openpic_unmask(dev, irq); + + sc = device_get_softc(dev); + + if (cpcht_irqmap != NULL && irq < 128 && + cpcht_irqmap[irq].ht_base > 0) { + mtx_lock_spin(&sc->sc_ht_mtx); + + /* Program the data port */ + out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, + 0x10 + (cpcht_irqmap[irq].ht_source << 1)); + + /* Unmask the interrupt */ + ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); + ht_irq &= ~HTAPIC_MASK; + out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); + + mtx_unlock_spin(&sc->sc_ht_mtx); + } + + openpic_cpcht_eoi(dev, irq); +} + +static void +openpic_cpcht_eoi(device_t dev, u_int irq) +{ + struct openpic_cpcht_softc *sc; + uint32_t off, mask; + + if (irq == 255) + return; + + sc = device_get_softc(dev); + + if (cpcht_irqmap != NULL && irq < 128 && + cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { + /* If this is an HT IRQ, acknowledge it at the remote APIC */ + + if (cpcht_irqmap[irq].apple_eoi) { + off = (cpcht_irqmap[irq].ht_source >> 3) & ~3; + mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f); + out32rb(cpcht_irqmap[irq].apple_eoi + off, mask); + } else { + mtx_lock_spin(&sc->sc_ht_mtx); + + out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, + 0x11 + (cpcht_irqmap[irq].ht_source << 1)); + out32rb(cpcht_irqmap[irq].ht_base + 4, + cpcht_irqmap[irq].eoi_data); + + mtx_unlock_spin(&sc->sc_ht_mtx); + } + } + + openpic_eoi(dev, irq); } diff --git a/sys/powerpc/powermac/cpchtvar.h b/sys/powerpc/powermac/cpchtvar.h deleted file mode 100644 index 270345f..0000000 --- a/sys/powerpc/powermac/cpchtvar.h +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * Copyright (C) 2008 Nathan Whitehorn - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _POWERPC_POWERMAC_CPCHTVAR_H_ -#define _POWERPC_POWERMAC_CPCHTVAR_H_ - -struct cpcpci_range { - u_int32_t pci_hi; - u_int32_t pci_mid; - u_int32_t pci_lo; - u_int32_t junk; - u_int32_t host_hi; - u_int32_t host_lo; - u_int32_t size_hi; - u_int32_t size_lo; -}; - -struct cpcpci_softc { - device_t sc_dev; - phandle_t sc_node; - vm_offset_t sc_data; - int sc_bus; - struct cpcpci_range sc_range[6]; - int sc_nrange; - int sc_iostart; - struct rman sc_io_rman; - struct rman sc_mem_rman; - bus_space_tag_t sc_iot; - bus_space_tag_t sc_memt; - bus_dma_tag_t sc_dmat; - struct ofw_bus_iinfo sc_pci_iinfo; -}; - -#endif /* _POWERPC_POWERMAC_CPCHTVAR_H_ */ diff --git a/sys/powerpc/powermac/uninorth.c b/sys/powerpc/powermac/uninorth.c index fb3c990..fca9234 100644 --- a/sys/powerpc/powermac/uninorth.c +++ b/sys/powerpc/powermac/uninorth.c @@ -41,6 +41,7 @@ #include <dev/pci/pcireg.h> #include <machine/bus.h> +#include <machine/intr_machdep.h> #include <machine/md_var.h> #include <machine/pio.h> #include <machine/resource.h> @@ -52,392 +53,438 @@ #include <vm/vm.h> #include <vm/pmap.h> -#include "pcib_if.h" +/* + * Driver for the Uninorth chip itself. + */ -#define UNINORTH_DEBUG 0 +static MALLOC_DEFINE(M_UNIN, "unin", "unin device information"); /* * Device interface. */ -static int uninorth_probe(device_t); -static int uninorth_attach(device_t); -/* - * Bus interface. - */ -static int uninorth_read_ivar(device_t, device_t, int, - uintptr_t *); -static struct resource * uninorth_alloc_resource(device_t bus, - device_t child, int type, int *rid, u_long start, - u_long end, u_long count, u_int flags); -static int uninorth_activate_resource(device_t bus, device_t child, - int type, int rid, struct resource *res); +static int unin_chip_probe(device_t); +static int unin_chip_attach(device_t); /* - * pcib interface. + * Bus interface. */ -static int uninorth_maxslots(device_t); -static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int, - u_int, int); -static void uninorth_write_config(device_t, u_int, u_int, u_int, - u_int, u_int32_t, int); -static int uninorth_route_interrupt(device_t, device_t, int); +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 phandle_t uninorth_get_node(device_t bus, device_t dev); +static ofw_bus_get_devinfo_t unin_chip_get_devinfo; /* - * Local routines. + * Local routines */ -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. */ -static device_method_t uninorth_methods[] = { +static device_method_t unin_chip_methods[] = { + /* Device interface */ - DEVMETHOD(device_probe, uninorth_probe), - DEVMETHOD(device_attach, uninorth_attach), + DEVMETHOD(device_probe, unin_chip_probe), + DEVMETHOD(device_attach, unin_chip_attach), /* Bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_read_ivar, uninorth_read_ivar), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), - DEVMETHOD(bus_alloc_resource, uninorth_alloc_resource), - DEVMETHOD(bus_activate_resource, uninorth_activate_resource), - - /* pcib interface */ - DEVMETHOD(pcib_maxslots, uninorth_maxslots), - DEVMETHOD(pcib_read_config, uninorth_read_config), - DEVMETHOD(pcib_write_config, uninorth_write_config), - DEVMETHOD(pcib_route_interrupt, uninorth_route_interrupt), - - /* ofw_bus interface */ - DEVMETHOD(ofw_bus_get_node, uninorth_get_node), + 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 uninorth_driver = { - "pcib", - uninorth_methods, - sizeof(struct uninorth_softc) +static driver_t unin_chip_driver = { + "unin", + unin_chip_methods, + sizeof(struct unin_chip_softc) }; -static devclass_t uninorth_devclass; +static devclass_t unin_chip_devclass; -DRIVER_MODULE(uninorth, nexus, uninorth_driver, uninorth_devclass, 0, 0); +DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0); -static int -uninorth_probe(device_t dev) +/* + * Add an interrupt to the dev's resource list if present + */ +static void +unin_chip_add_intr(phandle_t devnode, struct unin_chip_devinfo *dinfo) { - const char *type, *compatible; + int *intr; + int i, nintr; + phandle_t iparent; + int icells; + + if (dinfo->udi_ninterrupts >= 6) { + printf("unin: device has more than 6 interrupts\n"); + return; + } - type = ofw_bus_get_type(dev); - compatible = ofw_bus_get_compat(dev); + 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 (type == NULL || compatible == NULL) - return (ENXIO); + if (intr[0] == -1) + return; - if (strcmp(type, "pci") != 0) - return (ENXIO); + 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; - if (strcmp(compatible, "uni-north") == 0) { - device_set_desc(dev, "Apple UniNorth Host-PCI bridge"); - return (0); - } else if (strcmp(compatible,"u3-agp") == 0) { - device_set_desc(dev, "Apple U3 Host-AGP bridge"); - return (0); + for (i = 0; i < nintr; i+=icells) { + resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, + dinfo->udi_ninterrupts, intr[i], intr[i], 1); + + dinfo->udi_interrupts[dinfo->udi_ninterrupts] = intr[i]; + dinfo->udi_ninterrupts++; } - - return (ENXIO); } -static int -uninorth_attach(device_t dev) +static void +unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo) { - struct uninorth_softc *sc; - const char *compatible; - phandle_t node, child; - u_int32_t reg[2], busrange[2]; - struct uninorth_range *rp, *io, *mem[2]; - int nmem, i, error; - - node = ofw_bus_get_node(dev); + 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; + + 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) +{ + const char *name; - if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) + name = ofw_bus_get_name(dev); + + if (name == NULL) return (ENXIO); - if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0 + && strcmp(name, "u4") != 0) return (ENXIO); - sc->sc_u3 = 0; - compatible = ofw_bus_get_compat(dev); - if (strcmp(compatible,"u3-agp") == 0) - sc->sc_u3 = 1; - - sc->sc_dev = dev; - sc->sc_node = node; - if (sc->sc_u3) { - sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[1] + 0x800000, PAGE_SIZE); - sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1] + 0xc00000, PAGE_SIZE); - } else { - sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[0] + 0x800000, PAGE_SIZE); - sc->sc_data = (vm_offset_t)pmap_mapdev(reg[0] + 0xc00000, PAGE_SIZE); - } - sc->sc_bus = busrange[0]; - - bzero(sc->sc_range, sizeof(sc->sc_range)); - if (sc->sc_u3) { - /* - * On Apple U3 systems, we have an otherwise standard - * Uninorth controller driving AGP. The one difference - * is that it uses a new PCI ranges format, so do the - * translation. - */ - - struct uninorth_range64 range64[6]; - bzero(range64, sizeof(range64)); - - sc->sc_nrange = OF_getprop(node, "ranges", range64, - sizeof(range64)); - for (i = 0; range64[i].pci_hi != 0; i++) { - sc->sc_range[i].pci_hi = range64[i].pci_hi; - sc->sc_range[i].pci_mid = range64[i].pci_mid; - sc->sc_range[i].pci_lo = range64[i].pci_lo; - sc->sc_range[i].host = range64[i].host_lo; - sc->sc_range[i].size_hi = range64[i].size_hi; - sc->sc_range[i].size_lo = range64[i].size_lo; - } - } else { - sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, - sizeof(sc->sc_range)); - } + device_set_desc(dev, "Apple UniNorth System Controller"); + return (0); +} - if (sc->sc_nrange == -1) { - device_printf(dev, "could not get ranges\n"); - return (ENXIO); - } +static int +unin_chip_attach(device_t dev) +{ + 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 error, i = 0; - sc->sc_range[6].pci_hi = 0; - io = NULL; - nmem = 0; - - for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { - switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { - case OFW_PCI_PHYS_HI_SPACE_CONFIG: - break; - case OFW_PCI_PHYS_HI_SPACE_IO: - io = rp; - break; - case OFW_PCI_PHYS_HI_SPACE_MEM32: - mem[nmem] = rp; - nmem++; - break; - case OFW_PCI_PHYS_HI_SPACE_MEM64: - break; - } - } + sc = device_get_softc(dev); + root = ofw_bus_get_node(dev); - if (io == NULL) { - device_printf(dev, "can't find io range\n"); + if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) return (ENXIO); - } - sc->sc_io_rman.rm_type = RMAN_ARRAY; - sc->sc_io_rman.rm_descr = "UniNorth PCI I/O Ports"; - sc->sc_iostart = io->host; - if (rman_init(&sc->sc_io_rman) != 0 || - rman_manage_region(&sc->sc_io_rman, io->pci_lo, - io->pci_lo + io->size_lo - 1) != 0) { - panic("uninorth_attach: failed to set up I/O rman"); - } - if (nmem == 0) { - device_printf(dev, "can't find mem ranges\n"); - return (ENXIO); - } + if (strcmp(ofw_bus_get_name(dev), "u3") == 0 + || strcmp(ofw_bus_get_name(dev), "u4") == 0) + i = 1; /* #address-cells lies */ + + 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 PCI Memory"; + 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); } - for (i = 0; i < nmem; i++) { - error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, - mem[i]->pci_lo + mem[i]->size_lo - 1); - if (error) { - device_printf(dev, - "rman_manage_region() 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); } /* - * Enable the GMAC Ethernet cell if Open Firmware says it is - * used. + * Only map the first page, since that is where the registers + * of interest lie. */ - for (child = OF_child(node); child; child = OF_peer(child)) { - char compat[32]; + sc->sc_addr = (vm_offset_t)pmap_mapdev(sc->sc_physaddr, PAGE_SIZE); + + sc->sc_version = *(u_int *)sc->sc_addr; + device_printf(dev, "Version %d\n", sc->sc_version); + /* + * 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(); - } + 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); - ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); - - device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); } static int -uninorth_maxslots(device_t dev) +unin_chip_print_child(device_t dev, device_t child) { + struct unin_chip_devinfo *dinfo; + struct resource_list *rl; + int retval = 0; - return (PCI_SLOTMAX); -} + dinfo = device_get_ivars(child); + rl = &dinfo->udi_resources; -static u_int32_t -uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, - int width) -{ - struct uninorth_softc *sc; - vm_offset_t caoff; + retval += bus_print_child_header(dev, child); - sc = device_get_softc(dev); - caoff = sc->sc_data + (reg & 0x07); - - if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) { - switch (width) { - case 1: - return (in8rb(caoff)); - break; - case 2: - return (in16rb(caoff)); - break; - case 4: - return (in32rb(caoff)); - break; - } - } + retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); + + retval += bus_print_child_footer(dev, child); - return (0xffffffff); + return (retval); } static void -uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func, - u_int reg, u_int32_t val, int width) +unin_chip_probe_nomatch(device_t dev, device_t child) { - struct uninorth_softc *sc; - vm_offset_t caoff; - - sc = device_get_softc(dev); - caoff = sc->sc_data + (reg & 0x07); - - if (uninorth_enable_config(sc, bus, slot, func, reg)) { - switch (width) { - case 1: - out8rb(caoff, val); - break; - case 2: - out16rb(caoff, val); - break; - case 4: - out32rb(caoff, val); - break; - } + 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 int -uninorth_route_interrupt(device_t bus, device_t dev, int pin) + +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 uninorth_softc *sc; - struct ofw_pci_register reg; - uint32_t pintr, mintr; - uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; + 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); - 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); - - /* Maybe it's a real interrupt, not an intpin */ - if (pin > 4) - return (pin); - - device_printf(bus, "could not route pin %d for device %d.%d\n", - pin, pci_get_slot(dev), pci_get_function(dev)); - return (PCI_INVALID_IRQ); -} - -static int -uninorth_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) -{ - struct uninorth_softc *sc; + dinfo = device_get_ivars(child); - sc = device_get_softc(dev); + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; - switch (which) { - case PCIB_IVAR_DOMAIN: - *result = device_get_unit(dev); - return (0); - case PCIB_IVAR_BUS: - *result = sc->sc_bus; - return (0); - } + 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); + } - return (ENOENT); -} + rle->end = rle->end - 1; /* Hack? */ -static struct resource * -uninorth_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 uninorth_softc *sc; - struct resource *rv; - struct rman *rm; - int needactivate; + if (start < rle->start) + adjstart = rle->start; + else if (start > rle->end) + adjstart = rle->end; + else + adjstart = start; - needactivate = flags & RF_ACTIVE; - flags &= ~RF_ACTIVE; + if (end < rle->start) + adjend = rle->start; + else if (end > rle->end) + adjend = rle->end; + else + adjend = end; - sc = device_get_softc(bus); + adjcount = adjend - adjstart; - switch (type) { - case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; break; - case SYS_RES_IOPORT: - rm = &sc->sc_io_rman; - break; - case SYS_RES_IRQ: - return (bus_alloc_resource(bus, type, rid, start, end, count, - flags)); + /* 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)); + device_get_nameunit(child)); return (NULL); } - rv = rman_reserve_resource(rm, start, end, count, flags, child); + rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, + child); if (rv == NULL) { - device_printf(bus, "failed to reserve resource for %s\n", - device_get_nameunit(child)); + device_printf(bus, + "failed to reserve resource %#lx - %#lx (%#lx)" + " for %s\n", adjstart, adjend, adjcount, + device_get_nameunit(child)); return (NULL); } @@ -445,229 +492,96 @@ uninorth_alloc_resource(device_t bus, device_t child, int type, int *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)); + device_printf(bus, + "failed to activate resource for %s\n", + device_get_nameunit(child)); rman_release_resource(rv); return (NULL); - } - } + } + } return (rv); } static int -uninorth_activate_resource(device_t bus, device_t child, int type, int rid, - struct resource *res) +unin_chip_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) { - void *p; - struct uninorth_softc *sc; + 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)); + return (bus_activate_resource(bus, type, rid, res)); - if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { + if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { vm_offset_t start; - start = (vm_offset_t)rman_get_start(res); - /* - * For i/o-ports, convert the start address to the - * uninorth PCI i/o window - */ - if (type == SYS_RES_IOPORT) - start += sc->sc_iostart; + start = (vm_offset_t) rman_get_start(res); if (bootverbose) - printf("uninorth mapdev: start %x, len %ld\n", start, - rman_get_size(res)); + printf("unin mapdev: start %zx, len %ld\n", start, + rman_get_size(res)); - p = pmap_mapdev(start, (vm_size_t)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_le_tag); + rman_set_bustag(res, &bs_be_tag); rman_set_bushandle(res, (u_long)p); } return (rman_activate_resource(res)); } -static int -uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot, - u_int func, u_int reg) -{ - uint32_t cfgval; - uint32_t pass; - if (resource_int_value(device_get_name(sc->sc_dev), - device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) { - if (pass == slot) - return (0); - } - - if (sc->sc_bus == bus) { - /* - * No slots less than 11 on the primary bus - */ - if (slot < 11) - return (0); - - cfgval = (1 << slot) | (func << 8) | (reg & 0xfc); - } else { - cfgval = (bus << 16) | (slot << 11) | (func << 8) | - (reg & 0xfc) | 1; - } - - do { - out32rb(sc->sc_addr, cfgval); - } while (in32rb(sc->sc_addr) != cfgval); - - return (1); -} - -static phandle_t -uninorth_get_node(device_t bus, device_t dev) -{ - struct uninorth_softc *sc; - - sc = device_get_softc(bus); - /* We only have one child, the PCI bus, which needs our own node. */ - - return sc->sc_node; -} - -/* - * Driver to swallow UniNorth host bridges from the PCI bus side. - */ static int -unhb_probe(device_t dev) +unin_chip_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) { - - if (pci_get_class(dev) == PCIC_BRIDGE && - pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { - device_set_desc(dev, "Host to PCI bridge"); - device_quiet(dev); - return (-10000); + /* + * 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 (ENXIO); -} - -static int -unhb_attach(device_t dev) -{ - - return (0); + return (rman_deactivate_resource(res)); } -static device_method_t unhb_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, unhb_probe), - DEVMETHOD(device_attach, unhb_attach), - - { 0, 0 } -}; - -static driver_t unhb_driver = { - "unhb", - unhb_methods, - 1, -}; -static devclass_t unhb_devclass; - -DRIVER_MODULE(unhb, pci, unhb_driver, unhb_devclass, 0, 0); - - -/* - * Small stub driver for the Uninorth chip itself, to allow setting - * of various parameters and cell enables - */ -static struct unin_chip_softc *uncsc; -static void -unin_enable_gmac(void) +static struct resource_list * +unin_chip_get_resource_list (device_t dev, device_t child) { - volatile u_int *clkreg; - u_int32_t tmpl; - - if (uncsc == NULL) - panic("unin_enable_gmac: device not found"); - - clkreg = (void *)(uncsc->sc_addr + UNIN_CLOCKCNTL); - tmpl = inl(clkreg); - tmpl |= UNIN_CLOCKCNTL_GMAC; - outl(clkreg, tmpl); -} - -static int -unin_chip_probe(device_t dev) -{ - const char *name; - - name = ofw_bus_get_name(dev); - - if (name == NULL) - return (ENXIO); + struct unin_chip_devinfo *dinfo; - if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0) - return (ENXIO); - - device_set_desc(dev, "Apple UniNorth System Controller"); - return (0); + dinfo = device_get_ivars(child); + return (&dinfo->udi_resources); } -static int -unin_chip_attach(device_t dev) +static const struct ofw_bus_devinfo * +unin_chip_get_devinfo(device_t dev, device_t child) { - phandle_t node; - u_int reg[3]; - int i = 0; - - uncsc = device_get_softc(dev); - node = ofw_bus_get_node(dev); - - if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) - return (ENXIO); - - if (strcmp(ofw_bus_get_name(dev), "u3") == 0) - i = 1; /* #address-cells lies */ - - uncsc->sc_physaddr = reg[i]; - uncsc->sc_size = reg[i+1]; - - /* - * 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); - - uncsc->sc_version = *(u_int *)uncsc->sc_addr; - device_printf(dev, "Version %d\n", uncsc->sc_version); + struct unin_chip_devinfo *dinfo; - return (0); + dinfo = device_get_ivars(child); + return (&dinfo->udi_obdinfo); } -static device_method_t unin_chip_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, unin_chip_probe), - DEVMETHOD(device_attach, unin_chip_attach), - - { 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); - - - - diff --git a/sys/powerpc/powermac/uninorthpci.c b/sys/powerpc/powermac/uninorthpci.c new file mode 100644 index 0000000..a8e1f2c --- /dev/null +++ b/sys/powerpc/powermac/uninorthpci.c @@ -0,0 +1,543 @@ +/*- + * Copyright (C) 2002 Benno Rice. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <machine/bus.h> +#include <machine/intr_machdep.h> +#include <machine/md_var.h> +#include <machine/pio.h> +#include <machine/resource.h> + +#include <sys/rman.h> + +#include <powerpc/powermac/uninorthvar.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include "pcib_if.h" + +#define UNINORTH_DEBUG 0 + +/* + * Device interface. + */ +static int uninorth_probe(device_t); +static int uninorth_attach(device_t); + +/* + * Bus interface. + */ +static int uninorth_read_ivar(device_t, device_t, int, + uintptr_t *); +static struct resource * uninorth_alloc_resource(device_t bus, + device_t child, int type, int *rid, u_long start, + u_long end, u_long count, u_int flags); +static int uninorth_activate_resource(device_t bus, device_t child, + int type, int rid, struct resource *res); + +/* + * pcib interface. + */ +static int uninorth_maxslots(device_t); +static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int, + u_int, int); +static void uninorth_write_config(device_t, u_int, u_int, u_int, + u_int, u_int32_t, int); +static int uninorth_route_interrupt(device_t, device_t, int); + +/* + * OFW Bus interface + */ + +static phandle_t uninorth_get_node(device_t bus, device_t dev); + +/* + * Local routines. + */ +static int uninorth_enable_config(struct uninorth_softc *, u_int, + u_int, u_int, u_int); + +/* + * Driver methods. + */ +static device_method_t uninorth_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uninorth_probe), + DEVMETHOD(device_attach, uninorth_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, uninorth_read_ivar), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, uninorth_alloc_resource), + DEVMETHOD(bus_activate_resource, uninorth_activate_resource), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, uninorth_maxslots), + DEVMETHOD(pcib_read_config, uninorth_read_config), + DEVMETHOD(pcib_write_config, uninorth_write_config), + DEVMETHOD(pcib_route_interrupt, uninorth_route_interrupt), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, uninorth_get_node), + + { 0, 0 } +}; + +static driver_t uninorth_driver = { + "pcib", + uninorth_methods, + sizeof(struct uninorth_softc) +}; + +static devclass_t uninorth_devclass; + +DRIVER_MODULE(uninorth, nexus, uninorth_driver, uninorth_devclass, 0, 0); + +static int +uninorth_probe(device_t dev) +{ + const char *type, *compatible; + + type = ofw_bus_get_type(dev); + compatible = ofw_bus_get_compat(dev); + + if (type == NULL || compatible == NULL) + return (ENXIO); + + if (strcmp(type, "pci") != 0) + return (ENXIO); + + if (strcmp(compatible, "uni-north") == 0) { + device_set_desc(dev, "Apple UniNorth Host-PCI bridge"); + return (0); + } else if (strcmp(compatible, "u3-agp") == 0) { + device_set_desc(dev, "Apple U3 Host-AGP bridge"); + return (0); + } else if (strcmp(compatible, "u4-pcie") == 0) { + device_set_desc(dev, "IBM CPC945 PCI Express Root"); + return (0); + } + + return (ENXIO); +} + +static int +uninorth_attach(device_t dev) +{ + struct uninorth_softc *sc; + const char *compatible; + phandle_t node; + u_int32_t reg[3], busrange[2]; + struct uninorth_range *rp, *io, *mem[2]; + int nmem, i, error; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + + if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) + return (ENXIO); + + if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + return (ENXIO); + + sc->sc_ver = 0; + compatible = ofw_bus_get_compat(dev); + if (strcmp(compatible, "u3-agp") == 0) + sc->sc_ver = 3; + if (strcmp(compatible, "u4-pcie") == 0) + sc->sc_ver = 4; + + sc->sc_dev = dev; + sc->sc_node = node; + if (sc->sc_ver >= 3) { + sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[1] + 0x800000, PAGE_SIZE); + sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1] + 0xc00000, PAGE_SIZE); + } else { + sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[0] + 0x800000, PAGE_SIZE); + sc->sc_data = (vm_offset_t)pmap_mapdev(reg[0] + 0xc00000, PAGE_SIZE); + } + sc->sc_bus = busrange[0]; + + bzero(sc->sc_range, sizeof(sc->sc_range)); + if (sc->sc_ver >= 3) { + /* + * On Apple U3 systems, we have an otherwise standard + * Uninorth controller driving AGP. The one difference + * is that it uses a new PCI ranges format, so do the + * translation. + */ + + struct uninorth_range64 range64[6]; + bzero(range64, sizeof(range64)); + + sc->sc_nrange = OF_getprop(node, "ranges", range64, + sizeof(range64)); + for (i = 0; range64[i].pci_hi != 0; i++) { + sc->sc_range[i].pci_hi = range64[i].pci_hi; + sc->sc_range[i].pci_mid = range64[i].pci_mid; + sc->sc_range[i].pci_lo = range64[i].pci_lo; + sc->sc_range[i].host = range64[i].host_lo; + sc->sc_range[i].size_hi = range64[i].size_hi; + sc->sc_range[i].size_lo = range64[i].size_lo; + } + } else { + sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, + sizeof(sc->sc_range)); + } + + if (sc->sc_nrange == -1) { + device_printf(dev, "could not get ranges\n"); + return (ENXIO); + } + + sc->sc_range[6].pci_hi = 0; + io = NULL; + nmem = 0; + + for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_CONFIG: + break; + case OFW_PCI_PHYS_HI_SPACE_IO: + io = rp; + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + mem[nmem] = rp; + nmem++; + break; + case OFW_PCI_PHYS_HI_SPACE_MEM64: + break; + } + } + + if (io == NULL) { + device_printf(dev, "can't find io range\n"); + return (ENXIO); + } + sc->sc_io_rman.rm_type = RMAN_ARRAY; + sc->sc_io_rman.rm_descr = "UniNorth PCI I/O Ports"; + sc->sc_iostart = io->host; + if (rman_init(&sc->sc_io_rman) != 0 || + rman_manage_region(&sc->sc_io_rman, io->pci_lo, + io->pci_lo + io->size_lo - 1) != 0) { + panic("uninorth_attach: failed to set up I/O rman"); + } + + if (nmem == 0) { + device_printf(dev, "can't find mem ranges\n"); + return (ENXIO); + } + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "UniNorth PCI Memory"; + error = rman_init(&sc->sc_mem_rman); + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + return (error); + } + for (i = 0; i < nmem; i++) { + error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, + mem[i]->pci_lo + mem[i]->size_lo - 1); + if (error) { + device_printf(dev, + "rman_manage_region() failed. error = %d\n", error); + return (error); + } + } + + ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); + + device_add_child(dev, "pci", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +static int +uninorth_maxslots(device_t dev) +{ + + return (PCI_SLOTMAX); +} + +static u_int32_t +uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, + int width) +{ + struct uninorth_softc *sc; + vm_offset_t caoff; + + sc = device_get_softc(dev); + caoff = sc->sc_data + (reg & 0x07); + + if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) { + switch (width) { + case 1: + return (in8rb(caoff)); + break; + case 2: + return (in16rb(caoff)); + break; + case 4: + return (in32rb(caoff)); + break; + } + } + + return (0xffffffff); +} + +static void +uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, u_int32_t val, int width) +{ + struct uninorth_softc *sc; + vm_offset_t caoff; + + sc = device_get_softc(dev); + caoff = sc->sc_data + (reg & 0x07); + + if (uninorth_enable_config(sc, bus, slot, func, reg)) { + switch (width) { + case 1: + out8rb(caoff, val); + break; + case 2: + out16rb(caoff, val); + break; + case 4: + out32rb(caoff, val); + break; + } + } +} + +static int +uninorth_route_interrupt(device_t bus, device_t dev, int pin) +{ + struct uninorth_softc *sc; + struct ofw_pci_register reg; + uint32_t pintr, mintr; + 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); + + /* Maybe it's a real interrupt, not an intpin */ + if (pin > 4) + return (pin); + + device_printf(bus, "could not route pin %d for device %d.%d\n", + pin, pci_get_slot(dev), pci_get_function(dev)); + return (PCI_INVALID_IRQ); +} + +static int +uninorth_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct uninorth_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_DOMAIN: + *result = device_get_unit(dev); + return (0); + case PCIB_IVAR_BUS: + *result = sc->sc_bus; + return (0); + } + + return (ENOENT); +} + +static struct resource * +uninorth_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 uninorth_softc *sc; + struct resource *rv; + struct rman *rm; + int needactivate; + + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + + sc = device_get_softc(bus); + + switch (type) { + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + + case SYS_RES_IOPORT: + rm = &sc->sc_io_rman; + break; + + case SYS_RES_IRQ: + return (bus_alloc_resource(bus, 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, start, end, count, flags, child); + if (rv == NULL) { + device_printf(bus, "failed to reserve resource for %s\n", + 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 +uninorth_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + void *p; + struct uninorth_softc *sc; + + 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); + /* + * For i/o-ports, convert the start address to the + * uninorth PCI i/o window + */ + if (type == SYS_RES_IOPORT) + start += sc->sc_iostart; + + if (bootverbose) + printf("uninorth 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_le_tag); + rman_set_bushandle(res, (u_long)p); + } + + return (rman_activate_resource(res)); +} + +static int +uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot, + u_int func, u_int reg) +{ + uint32_t cfgval; + uint32_t pass; + + if (resource_int_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) { + if (pass == slot) + return (0); + } + + /* + * Issue type 0 configuration space accesses for the root bus. + * + * NOTE: On U4, issue only type 1 accesses. There is a secret + * PCI Express <-> PCI Express bridge not present in the device tree, + * and we need to route all of our configuration space through it. + */ + if (sc->sc_bus == bus && sc->sc_ver < 4) { + /* + * No slots less than 11 on the primary bus on U3 and lower + */ + if (slot < 11) + return (0); + + cfgval = (1 << slot) | (func << 8) | (reg & 0xfc); + } else { + cfgval = (bus << 16) | (slot << 11) | (func << 8) | + (reg & 0xfc) | 1; + } + + /* Set extended register bits on U4 */ + if (sc->sc_ver == 4) + cfgval |= (reg >> 8) << 28; + + do { + out32rb(sc->sc_addr, cfgval); + } while (in32rb(sc->sc_addr) != cfgval); + + return (1); +} + +static phandle_t +uninorth_get_node(device_t bus, device_t dev) +{ + struct uninorth_softc *sc; + + sc = device_get_softc(bus); + /* We only have one child, the PCI bus, which needs our own node. */ + + return sc->sc_node; +} + diff --git a/sys/powerpc/powermac/uninorthvar.h b/sys/powerpc/powermac/uninorthvar.h index 4480eb8..9625d42 100644 --- a/sys/powerpc/powermac/uninorthvar.h +++ b/sys/powerpc/powermac/uninorthvar.h @@ -63,17 +63,37 @@ struct uninorth_softc { bus_dma_tag_t sc_dmat; struct ofw_bus_iinfo sc_pci_iinfo; - int sc_u3; + int sc_ver; }; 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 @@ struct unin_chip_softc { /* * 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_ */ diff --git a/sys/powerpc/powerpc/openpic.c b/sys/powerpc/powerpc/openpic.c index 2526a9d..5a25fc0 100644 --- a/sys/powerpc/powerpc/openpic.c +++ b/sys/powerpc/powerpc/openpic.c @@ -100,6 +100,16 @@ openpic_attach(device_t dev) sc->sc_bt = rman_get_bustag(sc->sc_memr); sc->sc_bh = rman_get_bushandle(sc->sc_memr); + /* Reset the PIC */ + x = openpic_read(sc, OPENPIC_CONFIG); + x |= OPENPIC_CONFIG_RESET; + openpic_write(sc, OPENPIC_CONFIG, x); + + while (openpic_read(sc, OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET) { + powerpc_sync(); + DELAY(100); + } + x = openpic_read(sc, OPENPIC_FEATURE); switch (x & OPENPIC_FEATURE_VERSION_MASK) { case 1: |