summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/powermac/cpcht.c
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2010-05-16 15:18:25 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2010-05-16 15:18:25 +0000
commit95e9f935c778d480ae3972c330bd79608917f78e (patch)
tree96903f191716aa69b72b0de38716686fcc5210d7 /sys/powerpc/powermac/cpcht.c
parent0e169b2cc79c453acd04e46d2d7adbb230f9920a (diff)
downloadFreeBSD-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
Diffstat (limited to 'sys/powerpc/powermac/cpcht.c')
-rw-r--r--sys/powerpc/powermac/cpcht.c851
1 files changed, 518 insertions, 333 deletions
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, &reg,
- 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);
}
OpenPOWER on IntegriCloud