diff --git a/util/sconfig/main.c b/util/sconfig/main.c index 06e991e9fd..365edc41a6 100644 --- a/util/sconfig/main.c +++ b/util/sconfig/main.c @@ -20,7 +20,11 @@ extern int linenum; -/* Maintains list of all the unique chip structures for the board. */ +/* + * Maintains list of all the unique chip structures for the board. + * This is shared across base and override device trees since we need to + * generate headers for all chips added by both the trees. + */ static struct chip chip_header; /* @@ -131,6 +135,7 @@ static struct chip mainboard_chip = { static struct chip_instance mainboard_instance = { .id = 0, .chip = &mainboard_chip, + .ref_count = 2, }; /* This is the parent of all devices added by parsing the devicetree file. */ @@ -340,6 +345,54 @@ struct chip_instance *new_chip_instance(char *path) return instance; } +static void delete_chip_instance(struct chip_instance *ins) +{ + + if (ins->ref_count == 0) { + printf("ERROR: ref count for chip instance is zero!!\n"); + exit(1); + } + + if (--ins->ref_count) + return; + + struct chip *c = ins->chip; + + /* Get pointer to first instance of the chip. */ + struct chip_instance *i = c->instance; + + /* + * If chip instance to be deleted is the first instance, then update + * instance pointer of the chip as well. + */ + if (i == ins) { + c->instance = ins->next; + free(ins); + return; + } + + /* + * Loop through the instances list of the chip to find and remove the + * given instance. + */ + while (1) { + if (i == NULL) { + printf("ERROR: chip instance not found!\n"); + exit(1); + } + + if (i->next != ins) { + i = i->next; + continue; + } + + i->next = ins->next; + break; + } + + free(ins); +} + /* * Allocate a new bus for the provided device. * - If this is the first bus being allocated under this device, then its id @@ -408,6 +461,24 @@ static struct device *get_dev(struct bus *parent, int path_a, int path_b, return NULL; } +/* + * Add given node as child of the provided parent. If this is the first child of + * the parent, update parent->children pointer as well. + */ +static void set_new_child(struct bus *parent, struct device *child) +{ + struct device *c = parent->children; + if (c) { + while (c->sibling) + c = c->sibling; + c->sibling = child; + } else + parent->children = child; + + child->sibling = NULL; + child->parent = parent; +} + struct device *new_device(struct bus *parent, struct chip_instance *chip_instance, const int bustype, const char *devnum, @@ -444,14 +515,9 @@ struct device *new_device(struct bus *parent, new_d->enabled = enabled; new_d->chip_instance = chip_instance; + chip_instance->ref_count++; - struct device *c = parent->children; - if (c) { - while (c->sibling) - c = c->sibling; - c->sibling = new_d; - } else - parent->children = new_d; + set_new_child(parent, new_d); switch (bustype) { case PCI: @@ -506,9 +572,8 @@ struct device *new_device(struct bus *parent, return new_d; } -void add_resource(struct bus *bus, int type, int index, int base) +static void new_resource(struct device *dev, int type, int index, int base) { - struct device *dev = bus->dev; struct resource *r = S_ALLOC(sizeof(struct resource)); r->type = type; @@ -524,6 +589,11 @@ void add_resource(struct bus *bus, int type, int index, int base) } } +void add_resource(struct bus *bus, int type, int index, int base) +{ + new_resource(bus->dev, type, index, base); +} + void add_register(struct chip_instance *chip_instance, char *name, char *val) { struct reg *r = S_ALLOC(sizeof(struct reg)); @@ -917,6 +987,312 @@ static void parse_devicetree(const char *file, struct bus *parent) fclose(filec); } +/* + * Match device nodes from base and override tree to see if they are the same + * node. + */ +static int device_match(struct device *a, struct device *b) +{ + return ((a->path_a == b->path_a) && + (a->path_b == b->path_b) && + (a->bustype == b->bustype) && + (a->chip_instance->chip == + b->chip_instance->chip)); +} + +/* + * Walk through the override subtree in breadth-first manner starting at node to + * see if chip_instance pointer of the node is same as chip_instance pointer of + * override parent that is passed into the function. If yes, then update the + * chip_instance pointer of the node to chip_instance pointer of the base + * parent. + */ +static void update_chip_pointers(struct device *node, + struct chip_instance *base_parent_ci, + struct chip_instance *override_parent_ci) +{ + struct queue_entry *q_head = NULL; + + enqueue_tail(&q_head, node); + + while ((node = dequeue_head(&q_head))) { + if (node->chip_instance != override_parent_ci) + continue; + node->chip_instance = base_parent_ci; + add_children_to_queue(&q_head, node); + } +} + +/* + * Add resource to device. If resource is already present, then update its base + * and index. If not, then add a new resource to the device. + */ +static void update_resource(struct device *dev, struct resource *res) +{ + struct resource *base_res = dev->res; + + while (base_res) { + if (base_res->type == res->type) { + base_res->index = res->index; + base_res->base = res->base; + return; + } + base_res = base_res->next; + } + + new_resource(dev, res->type, res->index, res->base); +} + +/* + * Add register to chip instance. If register is already present, then update + * its value. If not, then add a new register to the chip instance. + */ +static void update_register(struct chip_instance *c, struct reg *reg) +{ + struct reg *base_reg = c->reg; + + while (base_reg) { + if (!strcmp(base_reg->key, reg->key)) { + base_reg->value = reg->value; + return; + } + base_reg = base_reg->next; + } + + add_register(c, reg->key, reg->value); +} + +static void override_devicetree(struct bus *base_parent, + struct bus *override_parent); + +/* + * Update the base device properties using the properties of override device. In + * addition to that, call override_devicetree for all the buses under the + * override device. + * + * Override Rules: + * +--------------------+--------------------------------------------+ + * | | | + * |struct device member| Rule | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | id | Unchanged. This is used to generate device | + * | | structure name in static.c. So, no need to | + * | | override. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | enabled | Copy enabled state from override device. | + * | | This allows variants to override device | + * | | state. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | subsystem_vendor | Copy from override device only if any one | + * | subsystem_device | of the ids is non-zero. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | inherit_subsystem | Copy from override device only if it is | + * | | non-zero. This allows variant to only | + * | | enable inherit flag for a device. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | path | Unchanged since these are same for both | + * | path_a | base and override device (Used for | + * | path_b | matching devices). | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | bustype | Unchanged since this is same for both base | + * | | and override device (User for matching | + * | | devices). | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | pci_irq_info | Unchanged. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | parent | Unchanged. This is meaningful only within | + * | sibling | the parse tree, hence not being copied. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | res | Each resource that is present in override | + * | | device is copied over to base device: | + * | | 1. If resource of same type is present in | + * | | base device, then index and base of the | + * | | resource is copied. | + * | | 2. If not, then a new resource is allocated| + * | | under the base device using type, index | + * | | and base from override res. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | chip_instance | Each register of chip_instance is copied | + * | | over from override device to base device: | + * | | 1. If register with same key is present in | + * | | base device, then value of the register | + * | | is copied. | + * | | 2. If not, then a new register is allocated| + * | | under the base chip_instance using key | + * | | and value from override register. | + * | | | + * +-----------------------------------------------------------------+ + * | | | + * | bus | Recursively call override_devicetree on | + * | last_bus | each bus of override device. It is assumed | + * | | that bus with id X under base device | + * | | to bus with id X under override device. If | + * | | override device has more buses than base | + * | | device, then new buses are allocated under | + * | | base device. | + * | | | + * +-----------------------------------------------------------------+ + */ +static void update_device(struct device *base_dev, struct device *override_dev) +{ + /* + * Copy the enabled state of override device to base device. This allows + * override tree to enable or disable a particular device. + */ + base_dev->enabled = override_dev->enabled; + + /* + * Copy subsystem vendor and device ids from override device to base + * device only if the ids are non-zero in override device. Else, honor + * the values in base device. + */ + if (override_dev->subsystem_vendor || + override_dev->subsystem_device) { + base_dev->subsystem_vendor = override_dev->subsystem_vendor; + base_dev->subsystem_device = override_dev->subsystem_device; + } + + /* + * Copy value of inherity_subsystem from override device to base device + * only if it is non-zero in override device. This allows override + * tree to only enable inhert flag for a device. + */ + if (override_dev->inherit_subsystem) + base_dev->inherit_subsystem = override_dev->inherit_subsystem; + + /* + * Copy resources of override device to base device. + * 1. If resource is already present in base device, then index and base + * of the resource will be copied over. + * 2. If resource is not already present in base device, a new resource + * will be allocated. + */ + struct resource *res = override_dev->res; + while (res) { + update_resource(base_dev, res); + res = res->next; + } + + /* + * Copy registers of override chip instance to base chip instance. + * 1. If register key is already present in base chip instance, then + * value for the register is copied over. + * 2. If register key is not already present in base chip instance, then + * a new register will be allocated. + */ + struct reg *reg = override_dev->chip_instance->reg; + while (reg) { + update_register(base_dev->chip_instance, reg); + reg = reg->next; + } + + /* + * Now that the device properties are all copied over, look at each bus + * of the override device and run override_devicetree in a recursive + * manner. The assumption here is that first bus of override device + * corresponds to first bus of base device and so on. If base device has + * lesser buses than override tree, then new buses are allocated for it. + */ + struct bus *override_bus = override_dev->bus; + struct bus *base_bus = base_dev->bus; + + while (override_bus) { + + /* + * If we have more buses in override tree device, then allocate + * a new bus for the base tree device as well. + */ + if (!base_bus) { + alloc_bus(base_dev); + base_bus = base_dev->last_bus; + } + + override_devicetree(base_dev->bus, override_dev->bus); + + override_bus = override_bus->next_bus; + base_bus = base_bus->next_bus; + } + + delete_chip_instance(override_dev->chip_instance); + override_dev->chip_instance = NULL; +} + +/* + * Perform copy of device and properties from override parent to base parent. + * This function walks through the override tree in a depth-first manner + * performing following actions: + * 1. If matching device is found in base tree, then copy the properties of + * override device to base tree device. Call override_devicetree recursively on + * the bus of override device. + * 2. If matching device is not found in base tree, then set override tree + * device as new child of base_parent and update the chip pointers in override + * device subtree to ensure the nodes do not point to override tree chip + * instance. + */ +static void override_devicetree(struct bus *base_parent, + struct bus *override_parent) +{ + struct device *base_child; + struct device *override_child = override_parent->children; + struct device *next_child; + + while (override_child) { + + /* Look for a matching device in base tree. */ + for (base_child = base_parent->children; + base_child; base_child = base_child->sibling) { + if (device_match(base_child, override_child)) + break; + } + + next_child = override_child->sibling; + + /* + * If matching device is found, copy properties of + * override_child to base_child. + */ + if (base_child) + update_device(base_child, override_child); + else { + /* + * If matching device is not found, set override_child + * as a new child of base_parent. + */ + set_new_child(base_parent, override_child); + /* + * Ensure all nodes in override tree pointing to + * override parent chip_instance now point to base + * parent chip_instance. + */ + update_chip_pointers(override_child, + base_parent->dev->chip_instance, + override_parent->dev->chip_instance); + } + + override_child = next_child; + } +} + int main(int argc, char **argv) { if ((argc < MANDATORY_ARG_COUNT) || (argc > TOTAL_ARG_COUNT)) @@ -931,6 +1307,8 @@ int main(int argc, char **argv) if (argc == TOTAL_ARG_COUNT) { override_devtree = argv[OVERRIDE_DEVICEFILE_ARG]; parse_devicetree(override_devtree, &override_root_bus); + + override_devicetree(&base_root_bus, &override_root_bus); } FILE *autogen = fopen(outputc, "w"); diff --git a/util/sconfig/sconfig.h b/util/sconfig/sconfig.h index 63b65fd372..d350f52569 100644 --- a/util/sconfig/sconfig.h +++ b/util/sconfig/sconfig.h @@ -58,6 +58,12 @@ struct chip_instance { /* Pointer to next instance of the same chip. */ struct chip_instance *next; + + /* + * Reference count - Indicates how many devices hold pointer to this + * chip instance. + */ + int ref_count; }; struct chip {