Author: nwhitehorn
Date: Sun May 16 15:18:25 2010
New Revision: 208149
URL: http://svn.freebsd.org/changeset/base/208149

Log:
  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

Added:
  head/sys/powerpc/powermac/uninorthpci.c   (contents, props changed)
Deleted:
  head/sys/powerpc/powermac/cpchtvar.h
Modified:
  head/sys/conf/files.powerpc
  head/sys/dev/pci/pci.c
  head/sys/powerpc/include/intr_machdep.h
  head/sys/powerpc/powermac/cpcht.c
  head/sys/powerpc/powermac/uninorth.c
  head/sys/powerpc/powermac/uninorthvar.h
  head/sys/powerpc/powerpc/openpic.c

Modified: head/sys/conf/files.powerpc
==============================================================================
--- head/sys/conf/files.powerpc Sun May 16 15:14:59 2010        (r208148)
+++ head/sys/conf/files.powerpc Sun May 16 15:18:25 2010        (r208149)
@@ -139,7 +139,8 @@ powerpc/powermac/openpic_macio.c optiona
 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

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c      Sun May 16 15:14:59 2010        (r208148)
+++ head/sys/dev/pci/pci.c      Sun May 16 15:18:25 2010        (r208149)
@@ -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, pcicfgreg
 {
 #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, pcicfgreg
                                        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);

Modified: head/sys/powerpc/include/intr_machdep.h
==============================================================================
--- head/sys/powerpc/include/intr_machdep.h     Sun May 16 15:14:59 2010        
(r208148)
+++ head/sys/powerpc/include/intr_machdep.h     Sun May 16 15:18:25 2010        
(r208149)
@@ -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;
 

Modified: head/sys/powerpc/powermac/cpcht.c
==============================================================================
--- head/sys/powerpc/powermac/cpcht.c   Sun May 16 15:14:59 2010        
(r208148)
+++ head/sys/powerpc/powermac/cpcht.c   Sun May 16 15:18:25 2010        
(r208149)
@@ -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(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
 
-DRIVER_MODULE(cpcpci, cpcht, cpcpci_driver, cpcpci_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)
+       if (type == NULL || compatible == NULL)
                return (ENXIO);
 
-       if (strcmp(type, "pci") != 0)
+       if (strcmp(type, "ht") != 0)
                return (ENXIO);
 
-       device_set_desc(dev, "HyperTransport-PCI bridge");
-       
+       if (strcmp(compatible, "u3-ht") != 0)
+               return (ENXIO);
+
+
+       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));
+       sc->sc_populated_slots = 0;
+       sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
 
-       if (sc->sc_nrange == -1) {
-               device_printf(dev, "could not get ranges\n");
-               return (ENXIO);
-       }
+       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);
+       }
+
+       /*
+        * 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));
+}
 
-       sc->sc_range[6].pci_hi = 0;
-       io = NULL;
-       nmem = 0;
+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;
+
+       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);
 
-       for (rp = sc->sc_range; rp->pci_hi != 0; rp++) {
+       /*
+        * 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 b
 }
 
 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 
 }
 
 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 
                *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, devi
 }
 
 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, d
                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, d
        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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to