PERFORCE change 171820 for review
Rafal Jaworowski
raj at FreeBSD.org
Tue Dec 15 14:46:55 PST 2009
http://p4web.freebsd.org/chv.cgi?CH=171820
Change 171820 by raj at 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 **)®);
- 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 **)®);
+ 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);
+}
More information about the p4-projects
mailing list