From 5ebc7c7e27780ce9a16289eeb87290eebd248ea9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:30:57 +0100 Subject: [PATCH 1/7] dm: core: Add of_alias_get_highest_id() The same functionality was added to Linux for i2c bus registration with this commit message: " of: base: add function to get highest id of an alias stem I2C supports adding adapters using either a dynamic or fixed id. The latter is provided by aliases in the DT case. To prevent id collisions of those two types, install this function which gives us the highest fixed id, so we can then let the dynamically created ones come after this highest number. Signed-off-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Wolfram Sang " Add it also to U-Boot for DM I2C support. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher Reviewed-by: Simon Glass --- drivers/core/of_access.c | 18 ++++++++++++++++++ include/dm/of_access.h | 10 ++++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 14c020a687..945b81448c 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -812,6 +812,24 @@ int of_alias_get_id(const struct device_node *np, const char *stem) return id; } +int of_alias_get_highest_id(const char *stem) +{ + struct alias_prop *app; + int id = -1; + + mutex_lock(&of_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (app->id > id) + id = app->id; + } + mutex_unlock(&of_mutex); + + return id; +} + struct device_node *of_get_stdout(void) { return of_stdout; diff --git a/include/dm/of_access.h b/include/dm/of_access.h index 5ed1a0cdb4..13fedb7cf5 100644 --- a/include/dm/of_access.h +++ b/include/dm/of_access.h @@ -424,6 +424,16 @@ int of_alias_scan(void); */ int of_alias_get_id(const struct device_node *np, const char *stem); +/** + * of_alias_get_highest_id - Get highest alias id for the given stem + * @stem: Alias stem to be examined + * + * The function travels the lookup table to get the highest alias id for the + * given alias stem. + * @return alias ID, if found, else -1 + */ +int of_alias_get_highest_id(const char *stem); + /** * of_get_stdout() - Get node to use for stdout * From 003c9dc89176cacc9996f737d08490805e29582e Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:30:58 +0100 Subject: [PATCH 2/7] fdt: Introduce fdtdec_get_alias_highest_id() Find out the highest alias ID used for certain subsystem. This call will be used for alocating IDs for i2c buses which are not described in DT. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher Reviewed-by: Simon Glass --- include/fdtdec.h | 13 +++++++++++++ lib/fdtdec.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/fdtdec.h b/include/fdtdec.h index f1bcbf837f..c2dd87ede2 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -625,6 +625,19 @@ int fdtdec_add_aliases_for_id(const void *blob, const char *name, int fdtdec_get_alias_seq(const void *blob, const char *base, int node, int *seqp); +/** + * Get the highest alias number for susbystem. + * + * It parses all aliases and find out highest recorded alias for subsystem. + * Aliases are of the form where is the sequence number. + * + * @param blob Device tree blob (if NULL, then error is returned) + * @param base Base name for alias susbystem (before the number) + * + * @return 0 highest alias ID, -1 if not found + */ +int fdtdec_get_alias_highest_id(const void *blob, const char *base); + /** * Get a property from the /chosen node * diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 18663ce6bd..55811975ef 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -549,6 +549,39 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, return -ENOENT; } +int fdtdec_get_alias_highest_id(const void *blob, const char *base) +{ + int base_len = strlen(base); + int prop_offset; + int aliases; + int max = -1; + + debug("Looking for highest alias id for '%s'\n", base); + + aliases = fdt_path_offset(blob, "/aliases"); + for (prop_offset = fdt_first_property_offset(blob, aliases); + prop_offset > 0; + prop_offset = fdt_next_property_offset(blob, prop_offset)) { + const char *prop; + const char *name; + int len, val; + + prop = fdt_getprop_by_offset(blob, prop_offset, &name, &len); + debug(" - %s, %s\n", name, prop); + if (*prop != '/' || prop[len - 1] || + strncmp(name, base, base_len)) + continue; + + val = trailing_strtol(name); + if (val > max) { + debug("Found seq %d\n", val); + max = val; + } + } + + return max; +} + const char *fdtdec_get_chosen_prop(const void *blob, const char *name) { int chosen_node; From 83e4c7e9ffa57fe4116967999c223c952a46a78a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:30:59 +0100 Subject: [PATCH 3/7] dm: core: Introduce dev_read_alias_highest_id() It is wrapper for calling of_alias_get_highest_id() when live tree is enabled and fdtdec_get_alias_highest_id() if not. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher Reviewed-by: Simon Glass --- drivers/core/read.c | 8 ++++++++ include/dm/read.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/core/read.c b/drivers/core/read.c index 3c46b3674e..6bda077a34 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -264,3 +264,11 @@ u64 dev_translate_address(struct udevice *dev, const fdt32_t *in_addr) { return ofnode_translate_address(dev_ofnode(dev), in_addr); } + +int dev_read_alias_highest_id(const char *stem) +{ + if (of_live_active()) + return of_alias_get_highest_id(stem); + + return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); +} diff --git a/include/dm/read.h b/include/dm/read.h index 389e30e7fb..60b727cbd8 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -510,6 +510,17 @@ int dev_read_resource_byname(struct udevice *dev, const char *name, * @return the translated address; OF_BAD_ADDR on error */ u64 dev_translate_address(struct udevice *dev, const fdt32_t *in_addr); + +/** + * dev_read_alias_highest_id - Get highest alias id for the given stem + * @stem: Alias stem to be examined + * + * The function travels the lookup table to get the highest alias id for the + * given alias stem. + * @return alias ID, if found, else -1 + */ +int dev_read_alias_highest_id(const char *stem); + #else /* CONFIG_DM_DEV_READ_INLINE is enabled */ static inline int dev_read_u32(struct udevice *dev, @@ -740,6 +751,11 @@ static inline u64 dev_translate_address(struct udevice *dev, const fdt32_t *in_a return ofnode_translate_address(dev_ofnode(dev), in_addr); } +static inline int dev_read_alias_highest_id(const char *stem) +{ + return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); +} + #endif /* CONFIG_DM_DEV_READ_INLINE */ /** From a93eb577b9ae635ef6f631e8509d113c15f53530 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:31:00 +0100 Subject: [PATCH 4/7] dm: core: Add tests for dev_read_alias_highest_id() It is checking the highest alias ID for eth, gpio, pci, i2c and error code on non existing alias. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher Reviewed-by: Simon Glass --- test/dm/test-fdt.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 984b80c02c..be16c99e17 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -219,6 +219,29 @@ static int dm_test_fdt(struct unit_test_state *uts) } DM_TEST(dm_test_fdt, 0); +static int dm_test_alias_highest_id(struct unit_test_state *uts) +{ + int ret; + + ret = dev_read_alias_highest_id("eth"); + ut_asserteq(5, ret); + + ret = dev_read_alias_highest_id("gpio"); + ut_asserteq(2, ret); + + ret = dev_read_alias_highest_id("pci"); + ut_asserteq(2, ret); + + ret = dev_read_alias_highest_id("i2c"); + ut_asserteq(0, ret); + + ret = dev_read_alias_highest_id("deadbeef"); + ut_asserteq(-1, ret); + + return 0; +} +DM_TEST(dm_test_alias_highest_id, 0); + static int dm_test_fdt_pre_reloc(struct unit_test_state *uts) { struct uclass *uc; From 6bfacc8aadb5d30a13413f337053118559a5ef25 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:31:01 +0100 Subject: [PATCH 5/7] i2c: dm: Record maximum id of devices before probing devices There is a need to find out the first free i2c ID which can be used for i2s buses (including i2c buses connected to i2c mux). Do it early in init and share this variable with other i2c classes for uniq bus identification. add from hs: fix build problem in i2c-uclass.c for omap devices Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/i2c-uclass.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 975318e5f2..7595b9b0f9 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -619,6 +619,30 @@ static int i2c_child_post_bind(struct udevice *dev) #endif } +struct i2c_priv { + int max_id; +}; + +int i2c_uclass_init(struct uclass *class) +{ + struct i2c_priv *priv = class->priv; + + /* Just for sure */ + if (!priv) + return -ENOMEM; + + /* Get the last allocated alias. */ +#if CONFIG_IS_ENABLED(OF_CONTROL) + priv->max_id = dev_read_alias_highest_id("i2c"); +#else + priv->max_id = -1; +#endif + + debug("%s: highest alias id is %d\n", __func__, priv->max_id); + + return 0; +} + UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", @@ -626,6 +650,8 @@ UCLASS_DRIVER(i2c) = { #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) .post_bind = dm_scan_fdt_dev, #endif + .init = i2c_uclass_init, + .priv_auto_alloc_size = sizeof(struct i2c_priv), .post_probe = i2c_post_probe, .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), .per_child_platdata_auto_alloc_size = sizeof(struct dm_i2c_chip), From 61607225d189f7d89a3ad6feba0b2f0e9bee5915 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:31:02 +0100 Subject: [PATCH 6/7] i2c: Fill req_seq in i2c_post_bind() For i2c controllers which are missing alias in DT there is no req_seq setup. This function is setting up proper ID based on highest found alias ID. On zcu102 this is the behavior when patch is applied. ZynqMP> i2c bus Bus 0: i2c@ff020000 20: gpio@20, offset len 1, flags 0 21: gpio@21, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 2: i2c@0 Bus 3: i2c@1 Bus 4: i2c@2 Bus 1: i2c@ff030000 (active 1) 74: i2c-mux@74, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 5: i2c@0 (active 5) 54: eeprom@54, offset len 1, flags 0 Bus 6: i2c@1 Bus 7: i2c@2 Bus 8: i2c@3 Bus 9: i2c@4 Bus 10: i2c@0 Bus 11: i2c@1 Bus 12: i2c@2 Bus 13: i2c@3 Bus 14: i2c@4 Bus 15: i2c@5 Bus 16: i2c@6 Bus 17: i2c@7 Before this patch applied (controllers have -1 ID) ZynqMP> i2c bus Bus 0: i2c@ff020000 20: gpio@20, offset len 1, flags 0 21: gpio@21, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus -1: i2c@0 Bus -1: i2c@1 Bus -1: i2c@2 Bus 1: i2c@ff030000 (active 1) 74: i2c-mux@74, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus -1: i2c@0 (active 0) 54: eeprom@54, offset len 1, flags 0 Bus -1: i2c@1 Bus -1: i2c@2 Bus -1: i2c@3 Bus -1: i2c@4 Bus -1: i2c@0 Bus -1: i2c@1 Bus -1: i2c@2 Bus -1: i2c@3 Bus -1: i2c@4 Bus -1: i2c@5 Bus -1: i2c@6 Bus -1: i2c@7 Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/i2c-uclass.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 7595b9b0f9..49e23a0a4b 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -623,6 +623,30 @@ struct i2c_priv { int max_id; }; +static int i2c_post_bind(struct udevice *dev) +{ + struct uclass *class = dev->uclass; + struct i2c_priv *priv = class->priv; + int ret = 0; + + /* Just for sure */ + if (!priv) + return -ENOMEM; + + debug("%s: %s, req_seq=%d\n", __func__, dev->name, dev->req_seq); + + /* if there is no alias ID, use the first free */ + if (dev->req_seq == -1) + dev->req_seq = ++priv->max_id; + + debug("%s: %s, new req_seq=%d\n", __func__, dev->name, dev->req_seq); + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + ret = dm_scan_fdt_dev(dev); +#endif + return ret; +} + int i2c_uclass_init(struct uclass *class) { struct i2c_priv *priv = class->priv; @@ -647,9 +671,7 @@ UCLASS_DRIVER(i2c) = { .id = UCLASS_I2C, .name = "i2c", .flags = DM_UC_FLAG_SEQ_ALIAS, -#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) - .post_bind = dm_scan_fdt_dev, -#endif + .post_bind = i2c_post_bind, .init = i2c_uclass_init, .priv_auto_alloc_size = sizeof(struct i2c_priv), .post_probe = i2c_post_probe, From c4bd12a7dad43ed9de3070c7c5e8b690d8c03a79 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 31 Jan 2019 16:31:03 +0100 Subject: [PATCH 7/7] i2c: mux: Generate longer i2c mux name For !DM case busses are listed as ZynqMP> i2c bus Bus 0: zynq_0 Bus 1: zynq_0->PCA9544A@0x75:0 Bus 2: zynq_0->PCA9544A@0x75:1 Bus 3: zynq_0->PCA9544A@0x75:2 Bus 4: zynq_1 Bus 5: zynq_1->PCA9548@0x74:0 Bus 6: zynq_1->PCA9548@0x74:1 Bus 7: zynq_1->PCA9548@0x74:2 Bus 8: zynq_1->PCA9548@0x74:3 Bus 9: zynq_1->PCA9548@0x74:4 Bus 10: zynq_1->PCA9548@0x75:0 Bus 11: zynq_1->PCA9548@0x75:1 Bus 12: zynq_1->PCA9548@0x75:2 Bus 13: zynq_1->PCA9548@0x75:3 Bus 14: zynq_1->PCA9548@0x75:4 Bus 15: zynq_1->PCA9548@0x75:5 Bus 16: zynq_1->PCA9548@0x75:6 Bus 17: zynq_1->PCA9548@0x75:7 where is exactly describing i2c bus topology. By moving to DM case i2c mux buses are using names from DT and because i2c-muxes describing sub busses with the same names like i2c@0, etc it is hard to identify which bus is where. Linux is adding topology information to i2c-mux busses to identify them better. This patch is doing the same and composing bus name with topology information. When patch is applied with topology information on zcu102-revA. ZynqMP> i2c bus Bus 0: i2c@ff020000 20: gpio@20, offset len 1, flags 0 21: gpio@21, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 2: i2c@ff020000->i2c-mux@75->i2c@0 Bus 3: i2c@ff020000->i2c-mux@75->i2c@1 Bus 4: i2c@ff020000->i2c-mux@75->i2c@2 Bus 1: i2c@ff030000 (active 1) 74: i2c-mux@74, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 5: i2c@ff030000->i2c-mux@74->i2c@0 (active 5) 54: eeprom@54, offset len 1, flags 0 Bus 6: i2c@ff030000->i2c-mux@74->i2c@1 Bus 7: i2c@ff030000->i2c-mux@74->i2c@2 Bus 8: i2c@ff030000->i2c-mux@74->i2c@3 Bus 9: i2c@ff030000->i2c-mux@74->i2c@4 Bus 10: i2c@ff030000->i2c-mux@75->i2c@0 Bus 11: i2c@ff030000->i2c-mux@75->i2c@1 Bus 12: i2c@ff030000->i2c-mux@75->i2c@2 Bus 13: i2c@ff030000->i2c-mux@75->i2c@3 Bus 14: i2c@ff030000->i2c-mux@75->i2c@4 Bus 15: i2c@ff030000->i2c-mux@75->i2c@5 Bus 16: i2c@ff030000->i2c-mux@75->i2c@6 Bus 17: i2c@ff030000->i2c-mux@75->i2c@7 Behavior before the patch is applied. ZynqMP> i2c bus Bus 0: i2c@ff020000 20: gpio@20, offset len 1, flags 0 21: gpio@21, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 2: i2c@0 Bus 3: i2c@1 Bus 4: i2c@2 Bus 1: i2c@ff030000 (active 1) 74: i2c-mux@74, offset len 1, flags 0 75: i2c-mux@75, offset len 1, flags 0 Bus 5: i2c@0 (active 5) 54: eeprom@54, offset len 1, flags 0 Bus 6: i2c@1 Bus 7: i2c@2 Bus 8: i2c@3 Bus 9: i2c@4 Bus 10: i2c@0 Bus 11: i2c@1 Bus 12: i2c@2 Bus 13: i2c@3 Bus 14: i2c@4 Bus 15: i2c@5 Bus 16: i2c@6 Bus 17: i2c@7 Signed-off-by: Michal Simek Reviewed-by: Simon Glass Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/i2c-mux-uclass.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-uclass.c b/drivers/i2c/muxes/i2c-mux-uclass.c index a680ee1762..8b1149997a 100644 --- a/drivers/i2c/muxes/i2c-mux-uclass.c +++ b/drivers/i2c/muxes/i2c-mux-uclass.c @@ -59,11 +59,34 @@ static int i2c_mux_post_bind(struct udevice *mux) dev_for_each_subnode(node, mux) { struct udevice *dev; const char *name; + const char *arrow = "->"; + char *full_name; + int parent_name_len, arrow_len, mux_name_len, name_len; name = ofnode_get_name(node); - ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name, - node, &dev); - debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL); + + /* Calculate lenghts of strings */ + parent_name_len = strlen(mux->parent->name); + arrow_len = strlen(arrow); + mux_name_len = strlen(mux->name); + name_len = strlen(name); + + full_name = calloc(1, parent_name_len + arrow_len + + mux_name_len + arrow_len + name_len + 1); + if (!full_name) + return -ENOMEM; + + /* Compose bus name */ + strcat(full_name, mux->parent->name); + strcat(full_name, arrow); + strcat(full_name, mux->name); + strcat(full_name, arrow); + strcat(full_name, name); + + ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", + full_name, node, &dev); + debug(" - bind ret=%d, %s, req_seq %d\n", ret, + dev ? dev->name : NULL, dev->req_seq); if (ret) return ret; }