svn commit: r208149 - in head/sys: conf dev/pci powerpc/include powerpc/powermac powerpc/powerpc

Nathan Whitehorn nwhitehorn at FreeBSD.org
Sun May 16 15:18:26 UTC 2010


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 ***


More information about the svn-src-head mailing list