int fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size) { pcell_t ranges[6], *rangesptr; pcell_t addr_cells, size_cells, par_addr_cells; u_long par_bus_addr, pbase, psize; int err, len, tuple_size, tuples; if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) return (ENXIO); /* * Process 'ranges' property. */ par_addr_cells = fdt_parent_addr_cells(node); if (par_addr_cells > 2) return (ERANGE); len = OF_getproplen(node, "ranges"); if (len > sizeof(ranges)) return (ENOMEM); if (len == 0) { *base = 0; *size = ULONG_MAX; return (0); } if (!(range_id < len)) return (ERANGE); if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) return (EINVAL); tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells + size_cells); tuples = len / tuple_size; if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) return (ERANGE); *base = 0; *size = 0; rangesptr = &ranges[range_id]; *base = fdt_data_get((void *)rangesptr, addr_cells); rangesptr += addr_cells; par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); rangesptr += par_addr_cells; err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, &pbase, &psize); if (err == 0) *base += pbase; else *base += par_bus_addr; *size = fdt_data_get((void *)rangesptr, size_cells); return (0); }
static int fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) { rman_res_t start, end, count; pcell_t *reg, *regptr; pcell_t addr_cells, size_cells; int tuple_size, tuples; int i, j, rv, bank; if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) return (ENXIO); tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); tuples = OF_getencprop_alloc_multi(node, "reg", tuple_size, (void **)®); debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); if (tuples <= 0) /* No 'reg' property in this node. */ return (0); regptr = reg; for (i = 0; i < tuples; i++) { bank = fdt_data_get((void *)reg, 1); di->di_bank = bank; reg += 1; /* Get address/size. */ start = count = 0; for (j = 0; j < addr_cells; j++) { start <<= 32; start |= reg[j]; } for (j = 0; j < size_cells; j++) { count <<= 32; count |= reg[addr_cells + j - 1]; } reg += addr_cells - 1 + size_cells; /* Calculate address range relative to VA base. */ start = sc->sc_banks[bank].kva + start; end = start + count - 1; debugf("reg addr bank = %d, start = %jx, end = %jx, " "count = %jx\n", bank, start, end, count); /* Use bank (CS) cell as rid. */ resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start, end, count); } rv = 0; OF_prop_free(regptr); return (rv); }
int fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start, u_long *count) { /* Address portion. */ if (addr_cells > 2) return (ERANGE); *start = fdt_data_get((void *)data, addr_cells); data += addr_cells; /* Size portion. */ if (size_cells > 2) return (ERANGE); *count = fdt_data_get((void *)data, size_cells); return (0); }
int fdt_regsize(phandle_t node, u_long *base, u_long *size) { pcell_t reg[4]; int addr_cells, len, size_cells; if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells)) return (ENXIO); if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg)) return (ENOMEM); len = OF_getprop(node, "reg", ®, sizeof(reg)); if (len <= 0) return (EINVAL); *base = fdt_data_get(®[0], addr_cells); *size = fdt_data_get(®[addr_cells], size_cells); return (0); }
static int fdt_lbc_reg_decode(phandle_t node, struct lbc_softc *sc, struct lbc_devinfo *di) { u_long start, end, count; pcell_t *reg, *regptr; pcell_t addr_cells, size_cells; int tuple_size, tuples; int i, rv, bank; if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) return (ENXIO); tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); if (tuples <= 0) /* No 'reg' property in this node. */ return (0); regptr = reg; for (i = 0; i < tuples; i++) { bank = fdt_data_get((void *)reg, 1); di->di_bank = bank; reg += 1; /* Get address/size. */ rv = fdt_data_to_res(reg, addr_cells - 1, size_cells, &start, &count); if (rv != 0) { resource_list_free(&di->di_res); goto out; } reg += addr_cells - 1 + size_cells; /* Calculate address range relative to VA base. */ start = sc->sc_banks[bank].kva + start; end = start + count - 1; debugf("reg addr bank = %d, start = %lx, end = %lx, " "count = %lx\n", bank, start, end, count); /* Use bank (CS) cell as rid. */ resource_list_add(&di->di_res, SYS_RES_MEMORY, bank, start, end, count); } rv = 0; out: free(regptr, M_OFWPROP); return (rv); }
static int lbc_attach(device_t dev) { struct lbc_softc *sc; struct lbc_devinfo *di; struct rman *rm; u_long offset, start, size; device_t cdev; phandle_t node, child; pcell_t *ranges, *rangesptr; int tuple_size, tuples; int par_addr_cells; int bank, error, i; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_mrid = 0; sc->sc_mres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mrid, RF_ACTIVE); if (sc->sc_mres == NULL) return (ENXIO); sc->sc_bst = rman_get_bustag(sc->sc_mres); sc->sc_bsh = rman_get_bushandle(sc->sc_mres); for (bank = 0; bank < LBC_DEV_MAX; bank++) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_BR(bank), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_OR(bank), 0); } /* * Initialize configuration register: * - enable Local Bus * - set data buffer control signal function * - disable parity byte select * - set ECC parity type * - set bus monitor timing and timer prescale */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LBCR, 0); /* * Initialize clock ratio register: * - disable PLL bypass mode * - configure LCLK delay cycles for the assertion of LALE * - set system clock divider */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LCRR, 0x00030008); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEDR, 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTESR, ~0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LTEIR, 0x64080001); sc->sc_irid = 0; sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_ires != NULL) { error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_MISC | INTR_MPSAFE, NULL, lbc_intr, sc, &sc->sc_icookie); if (error) { device_printf(dev, "could not activate interrupt\n"); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires); sc->sc_ires = NULL; } } sc->sc_ltesr = ~0; rangesptr = NULL; rm = &sc->sc_rman; rm->rm_type = RMAN_ARRAY; rm->rm_descr = "Local Bus Space"; rm->rm_start = 0UL; rm->rm_end = ~0UL; error = rman_init(rm); if (error) goto fail; error = rman_manage_region(rm, rm->rm_start, rm->rm_end); if (error) { rman_fini(rm); goto fail; } /* * Process 'ranges' property. */ node = ofw_bus_get_node(dev); if ((fdt_addrsize_cells(node, &sc->sc_addr_cells, &sc->sc_size_cells)) != 0) { error = ENXIO; goto fail; } par_addr_cells = fdt_parent_addr_cells(node); if (par_addr_cells > 2) { device_printf(dev, "unsupported parent #addr-cells\n"); error = ERANGE; goto fail; } tuple_size = sizeof(pcell_t) * (sc->sc_addr_cells + par_addr_cells + sc->sc_size_cells); tuples = OF_getprop_alloc(node, "ranges", tuple_size, (void **)&ranges); if (tuples < 0) { device_printf(dev, "could not retrieve 'ranges' property\n"); error = ENXIO; goto fail; } rangesptr = ranges; debugf("par addr_cells = %d, addr_cells = %d, size_cells = %d, " "tuple_size = %d, tuples = %d\n", par_addr_cells, sc->sc_addr_cells, sc->sc_size_cells, tuple_size, tuples); start = 0; size = 0; for (i = 0; i < tuples; i++) { /* The first cell is the bank (chip select) number. */ bank = fdt_data_get((void *)ranges, 1); if (bank < 0 || bank > LBC_DEV_MAX) { device_printf(dev, "bank out of range: %d\n", bank); error = ERANGE; goto fail; } ranges += 1; /* * Remaining cells of the child address define offset into * this CS. */ offset = fdt_data_get((void *)ranges, sc->sc_addr_cells - 1); ranges += sc->sc_addr_cells - 1; /* Parent bus start address of this bank. */ start = fdt_data_get((void *)ranges, par_addr_cells); ranges += par_addr_cells; size = fdt_data_get((void *)ranges, sc->sc_size_cells); ranges += sc->sc_size_cells; debugf("bank = %d, start = %lx, size = %lx\n", bank, start, size); sc->sc_banks[bank].addr = start + offset; sc->sc_banks[bank].size = size; /* * Attributes for the bank. * * XXX Note there are no DT bindings defined for them at the * moment, so we need to provide some defaults. */ sc->sc_banks[bank].width = 16; sc->sc_banks[bank].msel = LBCRES_MSEL_GPCM; sc->sc_banks[bank].decc = LBCRES_DECC_DISABLED; sc->sc_banks[bank].atom = LBCRES_ATOM_DISABLED; sc->sc_banks[bank].wp = 0; } /* * Initialize mem-mappings for the LBC banks (i.e. chip selects). */ error = lbc_banks_map(sc); if (error) goto fail; /* * Walk the localbus and add direct subordinates as our children. */ for (child = OF_child(node); child != 0; child = OF_peer(child)) { di = malloc(sizeof(*di), M_LBC, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&di->di_ofw, child) != 0) { free(di, M_LBC); device_printf(dev, "could not set up devinfo\n"); continue; } resource_list_init(&di->di_res); if (fdt_lbc_reg_decode(child, sc, di)) { device_printf(dev, "could not process 'reg' " "property\n"); ofw_bus_gen_destroy_devinfo(&di->di_ofw); free(di, M_LBC); continue; } fdt_lbc_fixup(child, sc, di); /* Add newbus device for this FDT node */ cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "could not add child: %s\n", di->di_ofw.obd_name); resource_list_free(&di->di_res); ofw_bus_gen_destroy_devinfo(&di->di_ofw); free(di, M_LBC); continue; } debugf("added child name='%s', node=%p\n", di->di_ofw.obd_name, (void *)child); device_set_ivars(cdev, di); } /* * Enable the LBC. */ lbc_banks_enable(sc); free(rangesptr, M_OFWPROP); return (bus_generic_attach(dev)); fail: free(rangesptr, M_OFWPROP); bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mrid, sc->sc_mres); return (error); }
int fdt_localbus_devmap(phandle_t dt_node, struct arm_devmap_entry *fdt_devmap, int banks_max_num, int *banks_added) { pcell_t ranges[MV_LOCALBUS_MAX_BANKS * MV_LOCALBUS_MAX_BANK_CELLS]; pcell_t *rangesptr; uint32_t tuple_size, bank; vm_paddr_t offset; vm_size_t size; int dev_num, addr_cells, size_cells, par_addr_cells, va_index, i, j, k; if ((fdt_addrsize_cells(dt_node, &addr_cells, &size_cells)) != 0) return (EINVAL); par_addr_cells = fdt_parent_addr_cells(dt_node); if (par_addr_cells > 2) { /* * Localbus devmap initialization error: unsupported parent * #addr-cells */ return (ERANGE); } tuple_size = (addr_cells + par_addr_cells + size_cells); if (tuple_size > MV_LOCALBUS_MAX_BANK_CELLS) return (ERANGE); tuple_size *= sizeof(pcell_t); dev_num = OF_getprop(dt_node, "ranges", ranges, sizeof(ranges)); if (dev_num <= 0) return (EINVAL); /* Calculate number of devices attached to bus */ dev_num = dev_num / tuple_size; /* * If number of ranges > max number of localbus devices, * additional entries will not be processed */ dev_num = MIN(dev_num, banks_max_num); rangesptr = &ranges[0]; j = 0; /* Process data from FDT */ for (i = 0; i < dev_num; i++) { /* First field is bank number */ bank = fdt_data_get((void *)rangesptr, 1); rangesptr += 1; if (bank > MV_LOCALBUS_MAX_BANKS) { /* Bank out of range */ rangesptr += ((addr_cells - 1) + par_addr_cells + size_cells); continue; } /* Find virtmap entry for this bank */ va_index = -1; for (k = 0; localbus_virtmap[k].bank >= 0; k++) { if (localbus_virtmap[k].bank == bank) { va_index = k; break; } } /* Check if virtmap entry was found */ if (va_index == -1) { rangesptr += ((addr_cells - 1) + par_addr_cells + size_cells); continue; } /* Remaining child's address fields are unused */ rangesptr += (addr_cells - 1); /* Parent address offset */ offset = fdt_data_get((void *)rangesptr, par_addr_cells); rangesptr += par_addr_cells; /* Last field is size */ size = fdt_data_get((void *)rangesptr, size_cells); rangesptr += size_cells; if (size > localbus_virtmap[va_index].size) { /* Not enough space reserved in virtual memory map */ continue; } fdt_devmap[j].pd_va = localbus_virtmap[va_index].va; fdt_devmap[j].pd_pa = offset; fdt_devmap[j].pd_size = size; fdt_devmap[j].pd_prot = VM_PROT_READ | VM_PROT_WRITE; fdt_devmap[j].pd_cache = PTE_NOCACHE; /* Copy data to structure used by localbus driver */ localbus_banks[bank].va = fdt_devmap[j].pd_va; localbus_banks[bank].pa = fdt_devmap[j].pd_pa; localbus_banks[bank].size = fdt_devmap[j].pd_size; localbus_banks[bank].mapped = 1; j++; } *banks_added = j; return (0); }
static int fdt_localbus_reg_decode(phandle_t node, struct localbus_softc *sc, struct localbus_devinfo *di) { u_long start, end, count; pcell_t *reg, *regptr; pcell_t addr_cells, size_cells; int tuple_size, tuples; int i, rv, bank; if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) return (ENXIO); tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); if (tuples <= 0) /* No 'reg' property in this node. */ return (0); regptr = reg; for (i = 0; i < tuples; i++) { bank = fdt_data_get((void *)regptr, 1); if (bank >= MV_LOCALBUS_MAX_BANKS) { device_printf(sc->sc_dev, "bank number [%d] out of " "range\n", bank); continue; } /* * If device doesn't have virtual to physical mapping don't add * resources */ if (!(sc->sc_banks[bank].mapped)) { device_printf(sc->sc_dev, "device [%d]: missing memory " "mapping\n", bank); continue; } di->di_bank = bank; regptr += 1; /* Get address/size. */ rv = fdt_data_to_res(regptr, addr_cells - 1, size_cells, &start, &count); if (rv != 0) { resource_list_free(&di->di_res); goto out; } /* Check if enough amount of memory is mapped */ if (sc->sc_banks[bank].size < count) { device_printf(sc->sc_dev, "device [%d]: not enough " "memory reserved\n", bank); continue; } regptr += addr_cells - 1 + size_cells; /* Calculate address range relative to VA base. */ start = sc->sc_banks[bank].va + start; end = start + count - 1; debugf("reg addr bank = %d, start = %lx, end = %lx, " "count = %lx\n", bank, start, end, count); /* Use bank (CS) cell as rid. */ resource_list_add(&di->di_res, SYS_RES_MEMORY, di->di_bank, start, end, count); } rv = 0; out: free(reg, M_OFWPROP); return (rv); }
static device_t newbus_pci_create(device_t dev_par, phandle_t dt_node, u_long par_base, u_long par_size) { pcell_t reg[3 + 2]; device_t dev_child; u_long start, end, count; struct fdtbus_devinfo *di; char *name, *type, *compat; int len; OF_getprop_alloc(dt_node, "device_type", 1, (void **)&type); if (!(type != NULL && strcmp(type, "pci") == 0)) { /* Only process 'pci' subnodes. */ free(type, M_OFWPROP); return (NULL); } OF_getprop_alloc(dt_node, "name", 1, (void **)&name); OF_getprop_alloc(OF_parent(dt_node), "compatible", 1, (void **)&compat); dev_child = device_add_child(dev_par, NULL, -1); if (dev_child == NULL) { free(name, M_OFWPROP); free(type, M_OFWPROP); free(compat, M_OFWPROP); return (NULL); } di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK); di->di_node = dt_node; di->di_name = name; di->di_type = type; di->di_compat = compat; resource_list_init(&di->di_res); /* * Produce and set SYS_RES_MEMORY resources. */ start = 0; count = 0; len = OF_getprop(dt_node, "reg", ®, sizeof(reg)); if (len > 0) { if (fdt_data_verify((void *)®[1], 2) != 0) { device_printf(dev_child, "'reg' address value out of " "range\n"); newbus_device_destroy(dev_child); dev_child = NULL; goto out; } start = fdt_data_get((void *)®[1], 2); if (fdt_data_verify((void *)®[3], 2) != 0) { device_printf(dev_child, "'reg' size value out of " "range\n"); newbus_device_destroy(dev_child); dev_child = NULL; goto out; } count = fdt_data_get((void *)®[3], 2); } /* Calculate address range relative to base. */ par_base &= 0x000ffffful; start &= 0x000ffffful; start += par_base + fdt_immr_va; if (count == 0) count = par_size; end = start + count - 1; debugf("start = 0x%08lx, end = 0x%08lx, count = 0x%08lx\n", start, end, count); if (count > par_size) { device_printf(dev_child, "'reg' size value out of range\n"); newbus_device_destroy(dev_child); dev_child = NULL; goto out; } resource_list_add(&di->di_res, SYS_RES_MEMORY, 0, start, end, count); /* * Set SYS_RES_IRQ resources. */ if (fdt_intr_to_rl(OF_parent(dt_node), &di->di_res, di->di_intr_sl)) { device_printf(dev_child, "could not process 'interrupts' " "property\n"); newbus_device_destroy(dev_child); dev_child = NULL; goto out; } device_set_ivars(dev_child, di); debugf("added child name='%s', node=%p\n", name, (void *)dt_node); out: return (dev_child); }
static int fdt_get_range_by_busaddr(phandle_t node, u_long addr, u_long *base, u_long *size) { pcell_t ranges[32], *rangesptr; pcell_t addr_cells, size_cells, par_addr_cells; u_long bus_addr, par_bus_addr, pbase, psize; int err, i, len, tuple_size, tuples; if (node == 0) { *base = 0; *size = ULONG_MAX; return (0); } if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) return (ENXIO); /* * Process 'ranges' property. */ par_addr_cells = fdt_parent_addr_cells(node); if (par_addr_cells > 2) { return (ERANGE); } len = OF_getproplen(node, "ranges"); if (len < 0) return (-1); if (len > sizeof(ranges)) return (ENOMEM); if (len == 0) { return (fdt_get_range_by_busaddr(OF_parent(node), addr, base, size)); } if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) return (EINVAL); tuple_size = addr_cells + par_addr_cells + size_cells; tuples = len / (tuple_size * sizeof(cell_t)); if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) return (ERANGE); *base = 0; *size = 0; for (i = 0; i < tuples; i++) { rangesptr = &ranges[i * tuple_size]; bus_addr = fdt_data_get((void *)rangesptr, addr_cells); if (bus_addr != addr) continue; rangesptr += addr_cells; par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); rangesptr += par_addr_cells; err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, &pbase, &psize); if (err > 0) return (err); if (err == 0) *base = pbase; else *base = par_bus_addr; *size = fdt_data_get((void *)rangesptr, size_cells); return (0); } return (EINVAL); }