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 **)®); - 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); +}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912152246.nBFMktGd073895>