summaryrefslogtreecommitdiff
path: root/util/sconfig/main.c
diff options
context:
space:
mode:
authorNico Huber <nico.huber@secunet.com>2020-06-03 10:20:07 -0700
committerTim Wawrzynczak <twawrzynczak@chromium.org>2020-09-11 17:34:01 +0000
commit8e1ea525d1ea25634db37bf93690e1479a1d748e (patch)
treeddc994a698ad9d28de4d8df7c2fd7b9c3521053e /util/sconfig/main.c
parentad7c8ffba97b74d70e139b3745ce02ae513d2ef2 (diff)
sconfig: Allow to link devices to other device's drivers
Rarely, the driver of one device needs to know about another device that can be anywhere in the device hierarchy. Current applications boil down to EEPROMs that store information that is consumed by some code (e.g. MAC address). The idea is to give device nodes in the `devicetree.cb` an alias that can later be used to link it to a device driver's `config` structure. The driver has to declare a field of type `struct device *`, e.g. struct some_chip_driver_config { DEVTREE_CONST struct device *needed_eeprom; }; In the devicetree, the referenced device gets an alias, e.g. device i2c 0x50 alias my_eeprom on end The author of the devicetree is free to choose any alias name that is unique in the devicetree. Later, when configuring the driver the alias can be used to link the device with the field of a driver's config: chip some/chip/driver use my_eeprom as needed_eeprom end Override devices can add an alias if it does not exist, but cannot change the alias for a device that already exists. Alias names are checked for conflicts both in the base tree and in the override tree. References are resolved after the tree is parsed so aliases and references do not need to be in a specific order in the tree. Change-Id: I058a319f9b968924fbef9485a96c9e3f900a3ee8 Signed-off-by: Nico Huber <nico.huber@secunet.com> Signed-off-by: Duncan Laurie <dlaurie@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/35456 Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'util/sconfig/main.c')
-rw-r--r--util/sconfig/main.c115
1 files changed, 103 insertions, 12 deletions
diff --git a/util/sconfig/main.c b/util/sconfig/main.c
index b0c32f677a..170acadec7 100644
--- a/util/sconfig/main.c
+++ b/util/sconfig/main.c
@@ -669,16 +669,41 @@ static void set_new_child(struct bus *parent, struct device *child)
child->parent = parent;
}
+static const struct device *find_alias(const struct device *const parent,
+ const char *const alias)
+{
+ if (parent->alias && !strcmp(parent->alias, alias))
+ return parent;
+
+ const struct bus *bus;
+ for (bus = parent->bus; bus; bus = bus->next_bus) {
+ const struct device *child;
+ for (child = bus->children; child; child = child->sibling) {
+ const struct device *const ret = find_alias(child, alias);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
struct device *new_device(struct bus *parent,
struct chip_instance *chip_instance,
const int bustype, const char *devnum,
- int status)
+ char *alias, int status)
{
char *tmp;
int path_a;
int path_b = 0;
struct device *new_d;
+ /* Check for alias name conflicts. */
+ if (alias && find_alias(&base_root_dev, alias)) {
+ printf("ERROR: Alias already exists: %s\n", alias);
+ exit(1);
+ }
+
path_a = strtol(devnum, &tmp, 16);
if (*tmp == '.') {
tmp++;
@@ -698,6 +723,7 @@ struct device *new_device(struct bus *parent,
new_d->path_a = path_a;
new_d->path_b = path_b;
+ new_d->alias = alias;
new_d->enabled = status & 0x01;
new_d->hidden = (status >> 1) & 0x01;
@@ -819,6 +845,35 @@ void add_register(struct chip_instance *chip_instance, char *name, char *val)
add_reg(&chip_instance->reg, name, val);
}
+void add_reference(struct chip_instance *const chip_instance,
+ char *const name, char *const alias)
+{
+ add_reg(&chip_instance->ref, name, alias);
+}
+
+static void set_reference(struct chip_instance *const chip_instance,
+ char *const name, char *const alias)
+{
+ const struct device *const dev = find_alias(&base_root_dev, alias);
+ if (!dev) {
+ printf("ERROR: Cannot find device alias '%s'.\n", alias);
+ exit(1);
+ }
+
+ char *const ref_name = S_ALLOC(strlen(dev->name) + 2);
+ sprintf(ref_name, "&%s", dev->name);
+ add_register(chip_instance, name, ref_name);
+}
+
+static void update_references(FILE *file, FILE *head, struct device *dev,
+ struct device *next)
+{
+ struct reg *ref;
+
+ for (ref = dev->chip_instance->ref; ref; ref = ref->next)
+ set_reference(dev->chip_instance, ref->key, ref->value);
+}
+
void add_slot_desc(struct bus *bus, char *type, char *length, char *designation,
char *data_width)
{
@@ -1203,16 +1258,12 @@ static void emit_chip_instance(FILE *fil, struct chip_instance *instance)
fprintf(fil, "};\n\n");
}
-static void emit_chips(FILE *fil)
+static void emit_chip_configs(FILE *fil)
{
struct chip *chip = chip_header.next;
struct chip_instance *instance;
int chip_id;
- emit_chip_headers(fil, chip);
-
- fprintf(fil, "\n#define STORAGE static __unused DEVTREE_CONST\n\n");
-
for (; chip; chip = chip->next) {
if (!chip->chiph_exists)
continue;
@@ -1337,9 +1388,9 @@ static void update_resource(struct device *dev, struct resource *res)
* 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)
+static void update_register(struct reg **const head, struct reg *reg)
{
- struct reg *base_reg = c->reg;
+ struct reg *base_reg = *head;
while (base_reg) {
if (!strcmp(base_reg->key, reg->key)) {
@@ -1349,7 +1400,7 @@ static void update_register(struct chip_instance *c, struct reg *reg)
base_reg = base_reg->next;
}
- add_register(c, reg->key, reg->value);
+ add_reg(head, reg->key, reg->value);
}
static void override_devicetree(struct bus *base_parent,
@@ -1422,6 +1473,19 @@ static void override_devicetree(struct bus *base_parent,
* | | |
* +-----------------------------------------------------------------+
* | | |
+ * | ref | Each reference that is present in override |
+ * | | device is copied over to base device with |
+ * | | the same rules as registers. |
+ * | | |
+ * +-----------------------------------------------------------------+
+ * | | |
+ * | alias | Base device alias is copied to override. |
+ * | | Override devices cannot change/remove an |
+ * | | existing alias, but they can add an alias |
+ * | | if one does not exist. |
+ * | | |
+ * +-----------------------------------------------------------------+
+ * | | |
* | 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 |
@@ -1492,10 +1556,34 @@ static void update_device(struct device *base_dev, struct device *override_dev)
*/
struct reg *reg = override_dev->chip_instance->reg;
while (reg) {
- update_register(base_dev->chip_instance, reg);
+ update_register(&base_dev->chip_instance->reg, reg);
reg = reg->next;
}
+ /* Copy references just as with registers. */
+ reg = override_dev->chip_instance->ref;
+ while (reg) {
+ update_register(&base_dev->chip_instance->ref, reg);
+ reg = reg->next;
+ }
+
+ /* Check for alias name conflicts. */
+ if (override_dev->alias && find_alias(&base_root_dev, override_dev->alias)) {
+ printf("ERROR: alias already exists: %s\n", override_dev->alias);
+ exit(1);
+ }
+
+ /*
+ * Copy alias from base device.
+ *
+ * Override devices cannot change/remove an existing alias,
+ * but they can add an alias to a device if one does not exist yet.
+ */
+ if (base_dev->alias)
+ override_dev->alias = base_dev->alias;
+ else
+ base_dev->alias = override_dev->alias;
+
/*
* Use probe list from override device in place of base device, in order
* to allow an override to remove a probe from the base device.
@@ -1631,12 +1719,15 @@ int main(int argc, char **argv)
fprintf(autogen, "#include <device/device.h>\n");
fprintf(autogen, "#include <device/pci.h>\n\n");
fprintf(autogen, "#include <static.h>\n");
-
- emit_chips(autogen);
+ emit_chip_headers(autogen, chip_header.next);
+ fprintf(autogen, "\n#define STORAGE static __unused DEVTREE_CONST\n\n");
walk_device_tree(autogen, autohead, &base_root_dev, inherit_subsystem_ids);
fprintf(autogen, "\n/* pass 0 */\n");
walk_device_tree(autogen, autohead, &base_root_dev, pass0);
+ walk_device_tree(autogen, autohead, &base_root_dev, update_references);
+ fprintf(autogen, "\n/* chip configs */\n");
+ emit_chip_configs(autogen);
fprintf(autogen, "\n/* pass 1 */\n");
walk_device_tree(autogen, autohead, &base_root_dev, pass1);