summaryrefslogtreecommitdiffstats
path: root/sys/dev/ofw
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2008-12-15 15:31:10 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2008-12-15 15:31:10 +0000
commitf0e837432d62958c713c7e6ebbb2adc52c8fd696 (patch)
treeca68e24ea5167314369eefd8755cc6038f9ec07e /sys/dev/ofw
parent907438a3fe46be01afa5020f515481b240da1a08 (diff)
downloadFreeBSD-src-f0e837432d62958c713c7e6ebbb2adc52c8fd696.zip
FreeBSD-src-f0e837432d62958c713c7e6ebbb2adc52c8fd696.tar.gz
Adapt parts of the sparc64 Open Firmware bus enumeration code (in particular,
the code for parsing interrupt maps) to PowerPC and reflect their new MI status by moving them to the shared dev/ofw directory. This commit also modifies the OFW PCI enumeration procedure on PowerPC to allow the bus to find non-firmware-enumerated devices that Apple likes to add, and adds some useful Open Firmware properties (compat and name) to the pnpinfo string of children on OFW SBus, EBus, PCI, and MacIO links. Because of the change to PCI enumeration on PowerPC, X has started working again on PPC machines with Grackle hostbridges. Reviewed by: marius Obtained from: sparc64
Diffstat (limited to 'sys/dev/ofw')
-rw-r--r--sys/dev/ofw/ofw_bus_subr.c151
-rw-r--r--sys/dev/ofw/ofw_bus_subr.h21
-rw-r--r--sys/dev/ofw/openfirm.h6
3 files changed, 178 insertions, 0 deletions
diff --git a/sys/dev/ofw/ofw_bus_subr.c b/sys/dev/ofw/ofw_bus_subr.c
index 7a394c1..19de2b97 100644
--- a/sys/dev/ofw/ofw_bus_subr.c
+++ b/sys/dev/ofw/ofw_bus_subr.c
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
* Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
* All rights reserved.
*
@@ -33,7 +34,9 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/errno.h>
+#include <sys/libkern.h>
+#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
@@ -71,6 +74,22 @@ ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
free(obd->obd_type, M_OFWPROP);
}
+int
+ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
+ size_t buflen)
+{
+ if (ofw_bus_get_name(child) != NULL) {
+ strlcat(buf, "name=", buflen);
+ strlcat(buf, ofw_bus_get_name(child), buflen);
+ }
+
+ if (ofw_bus_get_compat(child) != NULL) {
+ strlcat(buf, " compat=", buflen);
+ strlcat(buf, ofw_bus_get_compat(child), buflen);
+ }
+
+ return (0);
+};
const char *
ofw_bus_gen_get_compat(device_t bus, device_t dev)
@@ -126,3 +145,135 @@ ofw_bus_gen_get_type(device_t bus, device_t dev)
return (NULL);
return (obd->obd_type);
}
+
+static int
+ofw_bus_searchprop(phandle_t node, char *propname, void *buf, int buflen)
+{
+ int rv;
+
+ for (; node != 0; node = OF_parent(node)) {
+ if ((rv = OF_getprop(node, propname, buf, buflen)) != -1)
+ return (rv);
+ }
+ return (-1);
+}
+
+void
+ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
+{
+ pcell_t addrc;
+ int msksz;
+
+ if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
+ addrc = 2;
+ ii->opi_addrc = addrc * sizeof(pcell_t);
+
+ ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1,
+ (void **)&ii->opi_imap);
+ if (ii->opi_imapsz > 0) {
+ msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1,
+ (void **)&ii->opi_imapmsk);
+ /*
+ * Failure to get the mask is ignored; a full mask is used then.
+ * Barf on bad mask sizes, however.
+ */
+ if (msksz != -1 && msksz != ii->opi_addrc + intrsz) {
+ panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
+ "property!");
+ }
+ }
+
+}
+
+int
+ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
+ int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
+ void *maskbuf)
+{
+ int rv;
+
+ if (ii->opi_imapsz <= 0)
+ return (0);
+ KASSERT(regsz >= ii->opi_addrc,
+ ("ofw_bus_lookup_imap: register size too small: %d < %d",
+ regsz, ii->opi_addrc));
+ rv = OF_getprop(node, "reg", reg, regsz);
+ if (rv < regsz)
+ panic("ofw_bus_lookup_imap: could not get reg property");
+ return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
+ ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
+ mintrsz));
+}
+
+/*
+ * Map an interrupt using the firmware reg, interrupt-map and
+ * interrupt-map-mask properties.
+ * The interrupt property to be mapped must be of size intrsz, and pointed to
+ * by intr. The regs property of the node for which the mapping is done must
+ * be passed as regs. This property is an array of register specifications;
+ * the size of the address part of such a specification must be passed as
+ * physsz. Only the first element of the property is used.
+ * imap and imapsz hold the interrupt mask and it's size.
+ * imapmsk is a pointer to the interrupt-map-mask property, which must have
+ * a size of physsz + intrsz; it may be NULL, in which case a full mask is
+ * assumed.
+ * maskbuf must point to a buffer of length physsz + intrsz.
+ * The interrupt is returned in result, which must point to a buffer of length
+ * rintrsz (which gives the expected size of the mapped interrupt).
+ * Returns 1 if a mapping was found, 0 otherwise.
+ */
+int
+ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
+ void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
+ int rintrsz)
+{
+ phandle_t parent;
+ u_int8_t *ref = maskbuf;
+ u_int8_t *uiintr = intr;
+ u_int8_t *uiregs = regs;
+ u_int8_t *uiimapmsk = imapmsk;
+ u_int8_t *mptr;
+ pcell_t pintrsz;
+ int i, rsz, tsz;
+
+ rsz = -1;
+ if (imapmsk != NULL) {
+ for (i = 0; i < physsz; i++)
+ ref[i] = uiregs[i] & uiimapmsk[i];
+ for (i = 0; i < intrsz; i++)
+ ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
+ } else {
+ bcopy(regs, ref, physsz);
+ bcopy(intr, ref + physsz, intrsz);
+ }
+
+ mptr = imap;
+ i = imapsz;
+ tsz = physsz + intrsz + sizeof(phandle_t) + rintrsz;
+ while (i > 0) {
+ KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
+ bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
+ if (ofw_bus_searchprop(parent, "#interrupt-cells",
+ &pintrsz, sizeof(pintrsz)) == -1)
+ pintrsz = 1; /* default */
+ pintrsz *= sizeof(pcell_t);
+ if (pintrsz != rintrsz)
+ panic("ofw_bus_search_intrmap: expected interrupt cell "
+ "size incorrect: %d > %d", rintrsz, pintrsz);
+
+ /*
+ * XXX: Apple hardware uses a second cell to set information
+ * on the interrupt trigger type. This information should
+ * be used somewhere to program the PIC.
+ */
+
+ if (bcmp(ref, mptr, physsz + intrsz) == 0) {
+ bcopy(mptr + physsz + intrsz + sizeof(parent),
+ result, rintrsz);
+ return (1);
+ }
+ mptr += tsz;
+ i -= tsz;
+ }
+ return (0);
+}
diff --git a/sys/dev/ofw/ofw_bus_subr.h b/sys/dev/ofw/ofw_bus_subr.h
index 6096b3e..58dd587 100644
--- a/sys/dev/ofw/ofw_bus_subr.h
+++ b/sys/dev/ofw/ofw_bus_subr.h
@@ -37,9 +37,30 @@
#include "ofw_bus_if.h"
+#define ORIP_NOINT -1
+#define ORIR_NOTFOUND 0xffffffff
+
+struct ofw_bus_iinfo {
+ u_int8_t *opi_imap;
+ u_int8_t *opi_imapmsk;
+ int opi_imapsz;
+ pcell_t opi_addrc;
+};
+
int ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *, phandle_t);
void ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *);
+/* Helper method to report interesting OF properties in pnpinfo */
+int ofw_bus_gen_child_pnpinfo_str(device_t, device_t, char *, size_t);
+
+/* Routines for processing firmware interrupt maps */
+
+void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int);
+int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int,
+ void *, int, void *, int, void *);
+int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *,
+ void *, void *, int);
+
ofw_bus_get_compat_t ofw_bus_gen_get_compat;
ofw_bus_get_model_t ofw_bus_gen_get_model;
ofw_bus_get_name_t ofw_bus_gen_get_name;
diff --git a/sys/dev/ofw/openfirm.h b/sys/dev/ofw/openfirm.h
index a8c0633..414e848 100644
--- a/sys/dev/ofw/openfirm.h
+++ b/sys/dev/ofw/openfirm.h
@@ -69,6 +69,12 @@ typedef unsigned long cell_t;
typedef unsigned int ihandle_t;
typedef unsigned int phandle_t;
+/*
+ * Other than in Open Firmware calls, the size of a bus cell seems to be
+ * always the same.
+ */
+typedef u_int32_t pcell_t;
+
#ifdef _KERNEL
#include <sys/cdefs.h>
#include <sys/types.h>
OpenPOWER on IntegriCloud