static int i2c_bus_fdt_init(char *node_path, FDTMachineInfo *fdti, void *priv) { Object *parent; DeviceState *dev; char parent_node_path[DT_PATH_LENGTH]; char *node_name = qemu_devtree_get_node_name(fdti->fdt, node_path); DB_PRINT_NP(1, "\n"); /* FIXME: share this code with fdt_generic_util.c/fdt_init_qdev() */ if (qemu_devtree_getparent(fdti->fdt, parent_node_path, node_path)) { abort(); } while (!fdt_init_has_opaque(fdti, parent_node_path)) { fdt_init_yield(fdti); } parent = fdt_init_get_opaque(fdti, parent_node_path); dev = (DeviceState *)object_dynamic_cast(parent, TYPE_DEVICE); if (parent && dev) { while (!dev->realized) { fdt_init_yield(fdti); } DB_PRINT_NP(0, "parenting i2c bus to %s bus %s\n", parent_node_path, node_name); fdt_init_set_opaque(fdti, node_path, qdev_get_child_bus(dev, node_name)); } else { DB_PRINT_NP(0, "orphaning i2c bus\n"); } return 0; }
qemu_irq fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx, int *err, char *info) { void *fdt = fdti->fdt; int intc_phandle, intc_cells, idx, errl; char intc_node_path[DT_PATH_LENGTH]; Error *errp = NULL; DeviceState *intc; if (!err) { err = &errl; } intc_phandle = qemu_fdt_getprop_cell(fdt, node_path, "interrupt-parent", 0, true, &errp); if (errp) { goto fail; } if (qemu_fdt_get_node_by_phandle(fdt, intc_node_path, intc_phandle)) { goto fail; } intc_cells = qemu_fdt_getprop_cell(fdt, intc_node_path, "#interrupt-cells", 0, false, &errp); if (errp) { goto fail; } idx = qemu_fdt_getprop_cell(fdt, node_path, "interrupts", intc_cells * irq_idx, false, &errp); if (errp) { goto fail; } while (!fdt_init_has_opaque(fdti, intc_node_path)) { fdt_init_yield(fdti); } intc = DEVICE(fdt_init_get_opaque(fdti, intc_node_path)); if (!intc) { goto fail; } if (info) { char *node_name = qemu_fdt_get_node_name(fdt, intc_node_path); sprintf(info, "%d (%s)", idx, node_name); g_free((void *)node_name); } *err = 0; return qdev_get_gpio_in(intc, idx); fail: *err = 1; if (info) { sprintf(info, "(none)"); } return NULL; }
static void fdt_init_node(void *args) { struct FDTInitNodeArgs *a = args; char *node_path = a->node_path; FDTMachineInfo *fdti = a->fdti; g_free(a); char *all_compats = NULL, *compat, *node_name, *next_compat; int compat_len; #ifdef FDT_GENERIC_UTIL_ERR_DEBUG static int entry_index; int this_entry = entry_index++; #endif DB_PRINT("enter %d %s\n", this_entry, node_path); /* try instance binding first */ node_name = qemu_fdt_get_node_name(fdti->fdt, node_path); if (!node_name) { fprintf(stderr, "FDT: ERROR: nameless node: %s\n", node_path); } if (!fdt_init_inst_bind(node_path, fdti, node_name)) { goto exit; } /* fallback to compatibility binding */ all_compats = qemu_fdt_getprop(fdti->fdt, node_path, "compatible", &compat_len, false, NULL); if (!all_compats) { fprintf(stderr, "FDT: ERROR: no compatibility found for node %s/%s\n", node_path, node_name); DB_PRINT("exit %d\n", this_entry); fdti->routinesPending--; return; } compat = all_compats; try_next_compat: if (compat_len == 0) { goto invalidate; } if (!fdt_init_compat(node_path, fdti, compat)) { goto exit; } if (!fdt_init_qdev(node_path, fdti, compat)) { goto exit; } next_compat = rawmemchr(compat, '\0'); compat_len -= (next_compat + 1 - compat); if (compat_len > 0) { *next_compat = ' '; } compat = next_compat+1; goto try_next_compat; invalidate: fprintf(stderr, "FDT: Unsupported peripheral invalidated %s compatibilities %s\n", node_name, all_compats); qemu_fdt_setprop_string(fdti->fdt, node_path, "compatible", "invalidated"); exit: DB_PRINT("exit %d\n", this_entry); if (!fdt_init_has_opaque(fdti, node_path)) { fdt_init_set_opaque(fdti, node_path, NULL); } g_free(node_path); g_free(all_compats); fdti->routinesPending--; return; }
static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat) { int err; qemu_irq irq; hwaddr base; int offset; DeviceState *dev; char *dev_type = NULL; int is_intc; int i; dev = fdt_create_qdev_from_compat(compat, &dev_type); if (!dev) { DB_PRINT("no match found for %s\n", compat); return 1; } /* FIXME: attach to the sysbus instead */ object_property_add_child(container_get(qdev_get_machine(), "/unattached"), qemu_fdt_get_node_name(fdti->fdt, node_path), OBJECT(dev), NULL); fdt_init_set_opaque(fdti, node_path, dev); /* connect nic if appropriate */ static int nics; if (object_property_find(OBJECT(dev), "mac", NULL)) { qdev_set_nic_properties(dev, &nd_table[nics]); if (nd_table[nics].instantiated) { DB_PRINT("NIC instantiated: %s\n", dev_type); nics++; } } offset = fdt_path_offset(fdti->fdt, node_path); for (offset = fdt_first_property_offset(fdti->fdt, offset); offset != -FDT_ERR_NOTFOUND; offset = fdt_next_property_offset(fdti->fdt, offset)) { const char *propname; int len; const void *val = fdt_getprop_by_offset(fdti->fdt, offset, &propname, &len); propname = trim_vendor(propname); ObjectProperty *p = object_property_find(OBJECT(dev), propname, NULL); if (p) { DB_PRINT("matched property: %s of type %s, len %d\n", propname, p->type, len); } if (!p) { continue; } /* FIXME: handle generically using accessors and stuff */ if (!strcmp(p->type, "uint8") || !strcmp(p->type, "uint16") || !strcmp(p->type, "uint32") || !strcmp(p->type, "uint64")) { uint64_t offset = (!strcmp(propname, "reg")) ? fdt_get_parent_base(node_path, fdti) : 0; object_property_set_int(OBJECT(dev), get_int_be(val, len) + offset, propname, &error_abort); DB_PRINT("set property %s to %#llx\n", propname, (long long unsigned int)get_int_be(val, len)); } else if (!strcmp(p->type, "bool")) { object_property_set_bool(OBJECT(dev), !!get_int_be(val, len), propname, &error_abort); DB_PRINT("set property %s to %#llx\n", propname, (long long unsigned int)get_int_be(val, len)); } else if (!strncmp(p->type, "link", 4)) { char target_node_path[DT_PATH_LENGTH]; DeviceState *linked_dev; if (qemu_fdt_get_node_by_phandle(fdti->fdt, target_node_path, get_int_be(val, len))) { abort(); } while (!fdt_init_has_opaque(fdti, target_node_path)) { fdt_init_yield(fdti); } linked_dev = fdt_init_get_opaque(fdti, target_node_path); object_property_set_link(OBJECT(dev), OBJECT(linked_dev), propname, &error_abort); } else if (!strcmp(p->type, "string")) { object_property_set_str(OBJECT(dev), strndup(val, len), propname, &error_abort); } } qdev_init_nofail(dev); /* map slave attachment */ base = qemu_fdt_getprop_cell(fdti->fdt, node_path, "reg", 0, false, &error_abort); base += fdt_get_parent_base(node_path, fdti); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); { int len; fdt_get_property(fdti->fdt, fdt_path_offset(fdti->fdt, node_path), "interrupt-controller", &len); is_intc = len >= 0; DB_PRINT("is interrupt controller: %c\n", is_intc ? 'y' : 'n'); } /* connect irq */ for (i = 0; ; ++i) { char irq_info[1024]; irq = fdt_get_irq_info(fdti, node_path, i, &err, irq_info); /* INTCs inferr their top level, if no IRQ connection specified */ if (err && is_intc) { irq = fdti->irq_base; sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); fprintf(stderr, "FDT: (%s) connected top level irq %s\n", dev_type, irq_info); break; } if (!err) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); fprintf(stderr, "FDT: (%s) connected irq %s\n", dev_type, irq_info); } else { break; } } if (dev_type) { g_free(dev_type); } return 0; }
static qemu_irq fdt_get_gpio(FDTMachineInfo *fdti, char *node_path, int* cur_cell, qemu_irq input, const FDTGenericGPIOSet *gpio_set, const char *debug_success, bool *end) { void *fdt = fdti->fdt; uint32_t parent_phandle, parent_cells = 0, cells[32]; char parent_node_path[DT_PATH_LENGTH]; DeviceState *parent; int i; Error *errp = NULL; const char *reason; bool free_reason = false; const char *propname = gpio_set->names->propname; const char *cells_propname = gpio_set->names->cells_propname; cells[0] = 0; parent_phandle = qemu_fdt_getprop_cell(fdt, node_path, propname, (*cur_cell)++, false, &errp); if (errp) { reason = g_strdup_printf("Cant get phandle from \"%s\" property\n", propname); *end = true; free_reason = true; goto fail_silent; } if (qemu_devtree_get_node_by_phandle(fdt, parent_node_path, parent_phandle)) { *end = true; reason = "cant get node from phandle\n"; goto fail; } parent_cells = qemu_fdt_getprop_cell(fdt, parent_node_path, cells_propname, 0, false, &errp); if (errp) { *end = true; reason = g_strdup_printf("cant get the property \"%s\" from the " \ "parent \"%s\"\n", cells_propname, parent_node_path); free_reason = true; goto fail; } for (i = 0; i < parent_cells; ++i) { cells[i] = qemu_fdt_getprop_cell(fdt, node_path, propname, (*cur_cell)++, false, &errp); if (errp) { *end = true; reason = "cant get cell value"; goto fail; } } while (!fdt_init_has_opaque(fdti, parent_node_path)) { fdt_init_yield(fdti); } parent = DEVICE(fdt_init_get_opaque(fdti, parent_node_path)); if (!parent) { reason = "parent is not a device"; goto fail_silent; } while (!parent->realized) { fdt_init_yield(fdti); } { const FDTGenericGPIOConnection *fgg_con = NULL; uint16_t range, idx; const char *gpio_name = NULL; qemu_irq ret; if (object_dynamic_cast(OBJECT(parent), TYPE_FDT_GENERIC_GPIO)) { const FDTGenericGPIOSet *set; FDTGenericGPIOClass *parent_fggc = FDT_GENERIC_GPIO_GET_CLASS(parent); for (set = parent_fggc->controller_gpios; set && set->names; set++) { if (!strcmp(gpio_set->names->cells_propname, set->names->cells_propname)) { fgg_con = set->gpios; break; } } } /* FIXME: cells[0] is not always the fdt indexing match system */ idx = cells[0] & ~(1ul << 31); if (fgg_con) { range = fgg_con->range ? fgg_con->range : 1; while (!(idx >= fgg_con->fdt_index && idx < (fgg_con->fdt_index + range)) && fgg_con->name) { fgg_con++; } if (!fgg_con) { goto fail; } idx -= fgg_con->fdt_index; gpio_name = fgg_con->name; } if (input) { FDTIRQConnection *irq = g_new0(FDTIRQConnection, 1); bool (*merge_fn)(bool *, int) = qemu_irq_shared_or_handler; /* FIXME: I am kind of stealing here. Use the msb of the first * cell to indicate the merge function. This needs to be discussed * with device-tree community on how this should be done properly. */ if (cells[0] & (1 << 31)) { merge_fn = qemu_irq_shared_and_handler; } DB_PRINT_NP(1, "%s GPIO output %s[%d] on %s\n", debug_success, gpio_name ? gpio_name : "unnamed", idx, parent_node_path); *irq = (FDTIRQConnection) { .dev = parent, .name = gpio_name, .merge_fn = merge_fn, .i = idx, .irq = input, .sink_info = NULL, /* FIMXE */ .next = fdti->irqs }; fdti->irqs = irq; } ret = qdev_get_gpio_in_named(parent, gpio_name, idx); if (ret) { DB_PRINT_NP(1, "wiring GPIO input %s on %s ... \n", fgg_con ? fgg_con->name : "unnamed", parent_node_path); } return ret; } fail: fprintf(stderr, "%s Failed: %s\n", node_path, reason); fail_silent: if (free_reason) { g_free((void *)reason); } return NULL; } static void fdt_get_irq_info_from_intc(FDTMachineInfo *fdti, qemu_irq *ret, char *intc_node_path, uint32_t *cells, uint32_t num_cells, uint32_t max, Error **errp) { FDTGenericIntcClass *intc_fdt_class; DeviceState *intc; while (!fdt_init_has_opaque(fdti, intc_node_path)) { fdt_init_yield(fdti); } intc = DEVICE(fdt_init_get_opaque(fdti, intc_node_path)); if (!intc) { goto fail; } while (!intc->realized) { fdt_init_yield(fdti); } intc_fdt_class = FDT_GENERIC_INTC_GET_CLASS(intc); if (!intc_fdt_class) { goto fail; } intc_fdt_class->get_irq(FDT_GENERIC_INTC(intc), ret, cells, num_cells, max, errp); return; fail: error_setg(errp, "%s", __func__); }
static void fdt_init_node(void *args) { struct FDTInitNodeArgs *a = args; char *node_path = a->node_path; FDTMachineInfo *fdti = a->fdti; g_free(a); simple_bus_fdt_init(node_path, fdti); char *all_compats = NULL, *compat, *node_name, *next_compat, *device_type; int compat_len; DB_PRINT_NP(1, "enter\n"); /* try instance binding first */ node_name = qemu_devtree_get_node_name(fdti->fdt, node_path); DB_PRINT_NP(1, "node with name: %s\n", node_name ? node_name : "(none)"); if (!node_name) { printf("FDT: ERROR: nameless node: %s\n", node_path); } if (!fdt_init_inst_bind(node_path, fdti, node_name)) { DB_PRINT_NP(0, "instance bind successful\n"); goto exit; } /* fallback to compatibility binding */ all_compats = qemu_fdt_getprop(fdti->fdt, node_path, "compatible", &compat_len, false, NULL); if (!all_compats) { DB_PRINT_NP(0, "no compatibility found\n"); } for (compat = all_compats; compat && compat_len; compat = next_compat+1) { char *compat_prefixed = g_strdup_printf("compatible:%s", compat); if (!fdt_init_compat(node_path, fdti, compat_prefixed)) { goto exit; } g_free(compat_prefixed); if (!fdt_init_qdev(node_path, fdti, compat)) { goto exit; } next_compat = rawmemchr(compat, '\0'); compat_len -= (next_compat + 1 - compat); if (compat_len > 0) { *next_compat = ' '; } } device_type = qemu_fdt_getprop(fdti->fdt, node_path, "device_type", NULL, false, NULL); device_type = g_strdup_printf("device_type:%s", device_type); if (!fdt_init_compat(node_path, fdti, device_type)) { goto exit; } if (!all_compats) { goto exit; } DB_PRINT_NP(0, "FDT: Unsupported peripheral invalidated - " "compatibilities %s\n", all_compats); qemu_fdt_setprop_string(fdti->fdt, node_path, "compatible", "invalidated"); exit: DB_PRINT_NP(1, "exit\n"); if (!fdt_init_has_opaque(fdti, node_path)) { fdt_init_set_opaque(fdti, node_path, NULL); } g_free(node_path); g_free(all_compats); return; }