Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 15 Dec 2009 22:46:55 GMT
From:      Rafal Jaworowski <raj@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 171820 for review
Message-ID:  <200912152246.nBFMktGd073895@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/chv.cgi?CH=171820

Change 171820 by raj@raj_fdt on 2009/12/15 22:46:18

	Provide missing pieces for the simplebus driver. It now handles all
	fundamental operations and allows for getting SOC drivers to attach to
	it and actually work.
	
	- Retrieve MEM resources from the DT blob during simplebus children
	  enumeration and assign them to the child device res list.
	
	- Similarly handle IRQ resources: retrieve from the DT interrupt
	  domain, decode and assign to device res list.
	
	- Provide all missing bus I/F methods of the driver.
	
	- Simplify '#address-cells' handling.
	
	- Prefer using pcell_t (32-bit) instead of cell_t (depends on
	  underlying arch) as FDT cells are strictly 32-bit.

Affected files ...

.. //depot/projects/fdt/sys/powerpc/mpc85xx/simplebus.c#2 edit

Differences ...

==== //depot/projects/fdt/sys/powerpc/mpc85xx/simplebus.c#2 (text+ko) ====

@@ -39,6 +39,9 @@
 #include <sys/rman.h>
 #include <sys/malloc.h>
 
+#include <machine/intr_machdep.h>
+#include <machine/vmparam.h>
+
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 #include <dev/ofw/openfirm.h>
@@ -47,8 +50,8 @@
 
 #include "../../contrib/dtc/libfdt/libfdt_env.h"
 
+#define DEBUG
 #undef DEBUG
-#define DEBUG
 
 #ifdef DEBUG
 #define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
@@ -62,23 +65,51 @@
 struct simplebus_softc {
 	int	sc_addr_cells;
 	int	sc_size_cells;
-	u_long	sc_start;
+	u_long	sc_start_pa;
+	u_long	sc_start_va;
 	u_long	sc_size;
 };
 
+struct sense_level {
+	enum intr_trigger	trig;
+	enum intr_polarity	pol;
+};
+
+#define DI_MAX_INTR_NUM	8
+
 struct simplebus_devinfo {
 	struct ofw_bus_devinfo	di_ofw;
 	struct resource_list	di_res;
-	phandle_t		di_ipar;	/* interrupt-parent phandle */
+
+	/* Interrupt-parent phandle */
+	phandle_t		di_intr_par;
+	int			di_intr_cells;
+
+	/* Interrupts sense-level info for this device */
+	struct sense_level	di_intr_sl[DI_MAX_INTR_NUM];
+	int			di_intr_num;
 };
 
+
 /*
  * Prototypes.
  */
 static int simplebus_probe(device_t);
 static int simplebus_attach(device_t);
 
+static int simplebus_print_child(device_t, device_t);
+static int simplebus_setup_intr(device_t, device_t, struct resource *, int,
+    driver_filter_t *, driver_intr_t *, void *, void **);
+
+static struct resource *simplebus_alloc_resource(device_t, device_t, int,
+    int *, u_long, u_long, u_long, u_int);
+static struct resource_list *simplebus_get_resource_list(device_t, device_t);
+
+static ofw_bus_get_devinfo_t simplebus_get_devinfo;
 
+/*
+ * Bus interface definition.
+ */
 static device_method_t simplebus_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		simplebus_probe),
@@ -89,8 +120,22 @@
 	DEVMETHOD(device_resume,	bus_generic_resume),
 
 	/* Bus interface */
-	DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
-	DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
+	DEVMETHOD(bus_print_child,	simplebus_print_child),
+	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
+	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
+	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+	DEVMETHOD(bus_setup_intr,	simplebus_setup_intr),
+	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
+	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
+
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_devinfo,	simplebus_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 }
 };
@@ -124,42 +169,18 @@
 static int
 fdt_parent_addr_cells(phandle_t node)
 {
-	char buf[32];
-	phandle_t root;
-	cell_t addr_cells;
-	int cell_size, n;
+	pcell_t addr_cells;
 
-	/*
-	 * Find out #address-cells of the superior bus.
-	 */
-	if ((root = OF_finddevice("/")) == 0)
-		panic("no root node.");
+	/* Find out #address-cells of the superior bus. */
+	if (OF_searchprop(node, "#address-cells", &addr_cells,
+	    sizeof(addr_cells)) <= 0)
+		addr_cells = 1;
 
-	cell_size = sizeof(addr_cells);
-
-	while ((node = OF_parent(node)) != root && node != 0) {
-
-		OF_getprop(node, "name", buf, sizeof(buf));
-		debugf("node=%p, name=%s\n", (void *)node, buf);
-
-		if (OF_getprop(node, "#address-cells", &addr_cells,
-		    cell_size) < cell_size)
-			continue;
-
-		return ((int)addr_cells);
-	}
-
-	if (OF_getprop(root, "#address-cells", &addr_cells,
-	    cell_size) < cell_size)
-		n = 1;
-	else
-		n = (int)addr_cells;
-
-	return (n);
+	return ((int)addr_cells);
 }
 
 static void
-fdt_ranges_dump(cell_t *ranges, int tuples, int par_addr_cells,
+fdt_ranges_dump(pcell_t *ranges, int tuples, int par_addr_cells,
     int this_addr_cells, int this_size_cells)
 {
 #ifdef DEBUG
@@ -210,7 +231,7 @@
 }
 
 static int
-fdt_ranges_verify(cell_t *ranges, int tuples, int par_addr_cells,
+fdt_ranges_verify(pcell_t *ranges, int tuples, int par_addr_cells,
     int this_addr_cells, int this_size_cells)
 {
 	int i, rv, ulsz;
@@ -255,25 +276,18 @@
 simplebus_add_reg(phandle_t node, struct simplebus_devinfo *di,
     struct simplebus_softc *sc)
 {
-	cell_t *reg;
+	pcell_t *reg;
 	u_long start, end, count;
-	ssize_t prop_len;
 	int tuple_size, tuples;
 	int i;
 
-	prop_len = OF_getprop_alloc(node, "reg", 1, (void **)&reg);
-	if (prop_len <= 0)
+	/* XXX free reg prop? */
+	tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + sc->sc_size_cells);
+	tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)&reg);
+	if (tuples <= 0)
 		/* No 'reg' property in this node. */
 		return (0);
 
-	tuple_size = sizeof(cell_t) * (sc->sc_addr_cells + sc->sc_size_cells);
-
-	if (prop_len % tuple_size) {
-		debugf("inconsistent 'reg' property\n");
-		goto err;
-	}
-	tuples = prop_len / tuple_size;
-
 	for (i = 0; i < tuples; i++) {
 		/* Address portion. */
 		if (fdt_data_verify((void *)reg, sc->sc_addr_cells)) {
@@ -293,14 +307,14 @@
 		count = fdt_data_get((void *)reg, sc->sc_size_cells);
 		reg += sc->sc_size_cells;
 
-		/* Calculate address range relative to simple-bus base. */
-		start = sc->sc_start + start;
+		/* Calculate address range relative to simple-bus VA base. */
+		start = sc->sc_start_va + start;
 		end = start + count - 1;
 
 		debugf("reg addr start = %lx, end = %lx, count = %lx\n", start,
 		    end, count);
 
-		if (end > sc->sc_start + sc->sc_size) {
+		if (end > sc->sc_start_va + sc->sc_size) {
 			debugf("end out of simple-bus range: %lx\n", end);
 			goto err;
 		}
@@ -308,38 +322,211 @@
 		resource_list_add(&di->di_res, SYS_RES_MEMORY, i, start, end,
 		    count);
 	}
+	return (0);
 
-	return (0);
 err:
 	resource_list_free(&di->di_res);
 	return (ERANGE);
 }
 
 static int
-simplebus_add_intr(phandle_t node, struct simplebus_devinfo *di)
+fdt_pic_decode_iic(phandle_t node, pcell_t *intr, int intr_cells,
+    int *interrupt, int *trig, int *pol)
+{
+
+	/* TODO */
+	return (ENXIO);
+}
+
+static int
+fdt_pic_decode_openpic(phandle_t node, pcell_t *intr, int intr_cells,
+    int *interrupt, int *trig, int *pol)
 {
-	cell_t *intr;
+	char *compat;
 	ssize_t prop_len;
+	int rv;
 
-	prop_len = OF_getprop_alloc(node, "interrupts", 1, (void **)&intr);
+	prop_len = OF_getprop_alloc(node, "compatible", 1, (void **)&compat);
 	if (prop_len <= 0)
-		/* No 'interrupts' property in this node. */
+		return (ENXIO);
+
+	rv = 0;
+	if (strncmp("open-pic", compat, 8) < 0) {
+		rv = ENXIO;
+		goto out;
+	}
+
+	/*
+	 * XXX The interrupt number read out from the device tree is already
+	 * offset by 16 to reflect the 'internal' IRQ range shift on the
+	 * OpenPIC. We still however need to account for the ISA IRQ block,
+	 * potentially in use as well.
+	 */
+	*interrupt = ISA_IRQ_COUNT + intr[0];
+
+	if (intr_cells == 2)
+		switch (intr[1]) {
+		case 0:
+			/* L to H edge */
+			*trig = INTR_TRIGGER_EDGE;
+			*pol = INTR_POLARITY_HIGH;
+			break;
+		case 1:
+			/* Active L level */
+			*trig = INTR_TRIGGER_LEVEL;
+			*pol = INTR_POLARITY_LOW;
+			break;
+		case 2:
+			/* Active H level */
+			*trig = INTR_TRIGGER_LEVEL;
+			*pol = INTR_POLARITY_HIGH;
+			break;
+		case 3:
+			/* H to L edge */
+			*trig = INTR_TRIGGER_EDGE;
+			*pol = INTR_POLARITY_LOW;
+			break;
+		default:
+			*trig = INTR_TRIGGER_CONFORM;
+			*pol = INTR_POLARITY_CONFORM;
+		}
+	else {
+		*trig = INTR_TRIGGER_CONFORM;
+		*pol = INTR_POLARITY_CONFORM;
+	}
+
+out:
+	free(compat, M_OFWPROP);
+	return (rv);
+}
+
+typedef int (*fdt_pic_decode_t)(phandle_t, pcell_t *, int, int *, int *,
+    int *);
+
+static fdt_pic_decode_t fdt_pic_table[] = {
+	&fdt_pic_decode_iic,
+	&fdt_pic_decode_openpic,
+	NULL
+};
+
+static int
+fdt_intr_decode(struct simplebus_devinfo *di, pcell_t *intr,
+    int *interrupt, int *trig, int *pol)
+{
+	fdt_pic_decode_t intr_decode;
+	int i, rv;
+
+	for (i = 0; fdt_pic_table[i] != NULL; i++) {
+
+		/* XXX check if pic_handle has interrupt-controller prop? */
+
+		intr_decode = fdt_pic_table[i];
+		rv = intr_decode(di->di_intr_par, intr, di->di_intr_cells,
+		    interrupt, trig, pol);
+
+		if (rv == 0)
+			/* This was recognized as our PIC and decoded. */
+			return (0);
+	}
+
+	return (ENXIO);
+}
+
+static int
+simplebus_add_one_intr(struct simplebus_devinfo *di, pcell_t *intr)
+{
+	int interrupt, trig, pol;
+	int intr_num;
+
+	intr_num = di->di_intr_num;
+
+	if (intr_num > DI_MAX_INTR_NUM) {
+		debugf("max intr num reached: %d\n", DI_MAX_INTR_NUM);
+		return (ENOMEM);
+	}
+
+	interrupt = trig = pol = 0;
+
+	if (fdt_intr_decode(di, intr, &interrupt, &trig, &pol) != 0)
+		return (ENXIO);
+
+	if (interrupt <= 0)
+		return (ERANGE);
+
+	debugf("decoded intr = %d, trig = %d, pol = %d\n", interrupt, trig,
+	    pol);
+
+	di->di_intr_sl[intr_num].trig = trig;
+	di->di_intr_sl[intr_num].pol = pol;
+
+	resource_list_add(&di->di_res, SYS_RES_IRQ, intr_num,
+	    interrupt, interrupt, 1);
+
+	di->di_intr_num++;
+
+	return (0);
+}
+
+static int
+simplebus_add_intr(phandle_t node, struct simplebus_devinfo *di)
+{
+	phandle_t intr_par;
+	ihandle_t iph;
+	pcell_t *intr;
+	pcell_t intr_cells;
+	int i, intr_num, rv;
+
+	if (OF_getproplen(node, "interrupts") <= 0)
+		/* Node does not have 'interrupts' property. */
 		return (0);
 
+	/*
+	 * Find #interrupt-cells of the interrupt domain.
+	 */
+	if (OF_getprop(node, "interrupt-parent", &iph, sizeof(iph)) <= 0) {
+		debugf("no intr-parent phandle\n");
+		intr_par = OF_parent(node);
+	} else
+		intr_par = OF_instance_to_package(iph);
+
+	if (OF_getprop(intr_par, "#interrupt-cells", &intr_cells,
+	    sizeof(intr_cells)) <= 0) {
+		debugf("no intr-cells defined, defaulting to 1\n");
+		intr_cells = 1;
+	}
+
+	intr_num = OF_getprop_alloc(node, "interrupts",
+	    intr_cells * sizeof(pcell_t), (void **)&intr);
+	if (intr_num <= 0)
+		return (ERANGE);
+
+	rv = 0;
+	di->di_intr_num = 0;
+	di->di_intr_par = intr_par;
+	di->di_intr_cells = intr_cells;
+	for (i = 0; i < intr_num; i++) {
+		rv = simplebus_add_one_intr(di, &intr[i * intr_cells]);
+		if (rv != 0) {
+			/* XXX resource_list_free(&di->di_res); ??? */
+			goto out;
+		}
+	}
 
-	return (ENXIO);
+out:
+	free(intr, M_OFWPROP);
+	return (rv);
 }
 
 static int
 simplebus_attach(device_t dev)
 {
 	device_t cdev;
-	cell_t *ranges;
+	pcell_t *ranges;
 	u_long start, size;
 	struct simplebus_devinfo *di;
 	struct simplebus_softc *sc;
 	phandle_t node, child;
-	cell_t cell;
+	pcell_t cell;
 	ssize_t prop_len;
 	int cell_size, tuple_size, tuples;
 	int par_addr_cells;
@@ -375,6 +562,7 @@
 	tuple_size = cell_size * (sc->sc_addr_cells + par_addr_cells +
 	    sc->sc_size_cells);
 
+	/* XXX free ranges prop? */
 	prop_len = OF_getprop_alloc(node, "ranges", 1, (void **)&ranges);
 
 	start = 0;
@@ -405,7 +593,8 @@
 		size = fdt_data_get((void *)ranges, sc->sc_size_cells);
 	}
 	debugf("start = %lx, size = %lx\n", start, size);
-	sc->sc_start = start;
+	sc->sc_start_pa = start;
+	sc->sc_start_va = CCSRBAR_VA;
 	sc->sc_size = size;
 
 	/*
@@ -449,9 +638,116 @@
 			free(di, M_SIMPLEBUS);
 			continue;
 		}
-		debugf("added child: %s\n", di->di_ofw.obd_name);
 		device_set_ivars(cdev, di);
 	}
 
 	return (bus_generic_attach(dev));
 }
+
+static int
+simplebus_print_child(device_t dev, device_t child)
+{
+        struct simplebus_devinfo *di;
+        struct resource_list *rl;
+        int rv;
+
+        di = device_get_ivars(child);
+        rl = &di->di_res;
+
+	rv = 0;
+        rv += bus_print_child_header(dev, child);
+        rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
+        rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
+        rv += bus_print_child_footer(dev, child);
+
+        return (rv);
+}
+
+static struct resource *
+simplebus_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 simplebus_devinfo *di;
+	struct resource_list_entry *rle;
+
+	debugf("type = %d, rid = %d\n", type, *rid);
+
+	/*
+	 * Request for the default allocation with a given rid: use resource
+	 * list stored in the local device info.
+	 */
+	if ((start == 0UL) && (end == ~0UL) && (count == 1)) {
+		if ((di = device_get_ivars(child)) == NULL)
+			return (NULL);
+
+		rle = resource_list_find(&di->di_res, type, *rid);
+		if (rle == NULL) {
+			device_printf(bus, "no default resources for "
+			    "rid = %d\n", *rid);
+			return (NULL);
+		}
+		start = rle->start;
+		end = rle->end;
+		count = rle->count;
+	}
+
+	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+	    count, flags));
+}
+
+static struct resource_list *
+simplebus_get_resource_list(device_t bus, device_t child)
+{
+	struct simplebus_devinfo *di;
+
+	di = device_get_ivars(child);
+	return (&di->di_res);
+}
+
+static int
+simplebus_setup_intr(device_t bus, device_t child, struct resource *res,
+    int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg,
+    void **cookiep)
+{
+	struct simplebus_devinfo *di;
+	enum intr_trigger trig;
+	enum intr_polarity pol;
+	int irq, rid, err;
+
+	if (res == NULL)
+		panic("simplebus_setup_intr: NULL irq resource!");
+
+	rid = rman_get_rid(res);
+	if (rid > DI_MAX_INTR_NUM) {
+		device_printf(child, "rid out of range rid = %d\n", rid);
+		return (ERANGE);
+	}
+
+	irq = rman_get_start(res);
+
+	if ((di = device_get_ivars(child)) == NULL) {
+		device_printf(child, "could not retrieve devinfo\n");
+		return (ENXIO);
+	}
+
+	trig = di->di_intr_sl[rid].trig;
+	pol = di->di_intr_sl[rid].pol;
+
+	debugf("intr config: irq = %d, trig = %d, pol = %d\n", irq, trig, pol);
+
+	err = powerpc_config_intr(irq, trig, pol);
+	if (err)
+		return (err);
+
+	return (bus_generic_setup_intr(bus, child, res, flags, filter, ihand,
+	    arg, cookiep));
+}
+
+static const struct ofw_bus_devinfo *
+simplebus_get_devinfo(device_t bus, device_t child)
+{
+	struct simplebus_devinfo *di;
+
+	di = device_get_ivars(child);
+	return (&di->di_ofw);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912152246.nBFMktGd073895>