Previously, in order for the `pinctrl-*` DT node properties to be properly processed, the pinctrl's subnodes were limited to only having the `pinmux` property as well as other additional properties (slew-rate, bias-disable, etc.). Now, with this patch the pinctrl driver is made to work similarly to the one from Linux. It can now distinguish between one subnode and a subnode with multiple subnodes.
Signed-off-by: Sergiu Moga <sergiu.m...@microchip.com> --- drivers/pinctrl/pinctrl-at91-pio4.c | 73 ++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index e434a38f1f..50e3dd449a 100644 --- a/drivers/pinctrl/pinctrl-at91-pio4.c +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -15,6 +15,7 @@ #include <linux/bitops.h> #include <linux/io.h> #include <linux/err.h> +#include <dm/uclass-internal.h> #include <mach/atmel_pio4.h> DECLARE_GLOBAL_DATA_PTR; @@ -151,12 +152,11 @@ static inline struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev, #define MAX_PINMUX_ENTRIES 40 -static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config) +static int atmel_process_config_dev(struct udevice *dev, struct udevice *config) { struct atmel_pio4_plat *plat = dev_get_plat(dev); - struct atmel_pio4_port *bank_base; - const void *blob = gd->fdt_blob; int node = dev_of_offset(config); + struct atmel_pio4_port *bank_base; u32 offset, func, bank, line; u32 cells[MAX_PINMUX_ENTRIES]; u32 i, conf; @@ -164,18 +164,17 @@ static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config) conf = atmel_pinctrl_get_pinconf(config, plat); - count = fdtdec_get_int_array_count(blob, node, "pinmux", + /* + * The only case where this function returns a negative error value + * is when there is no "pinmux" property attached to this node + */ + count = fdtdec_get_int_array_count(gd->fdt_blob, node, "pinmux", cells, ARRAY_SIZE(cells)); - if (count < 0) { - printf("%s: bad pinmux array %d\n", __func__, count); - return -EINVAL; - } + if (count < 0) + return count; - if (count > MAX_PINMUX_ENTRIES) { - printf("%s: unsupported pinmux array count %d\n", - __func__, count); + if (count > MAX_PINMUX_ENTRIES) return -EINVAL; - } for (i = 0 ; i < count; i++) { offset = ATMEL_GET_PIN_NO(cells[i]); @@ -195,6 +194,56 @@ static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config) return 0; } +static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config) +{ + int node = dev_of_offset(config); + struct udevice *subconfig; + int subnode, subnode_count = 0, ret; + + /* + * If this function returns a negative error code then that means + * that either the "pinmux" property of the node is missing, which is + * the case for pinctrl nodes that do not have all the pins with the + * same configuration and are split in multiple subnodes, or something + * else went wrong and we have to stop. For the latter case, it would + * mean that the node failed even though it has no subnodes. + */ + ret = atmel_process_config_dev(dev, config); + if (!ret) + return ret; + + /* + * If we reach here, it means that the subnode pinctrl's DT has multiple + * subnodes. If it does not, then something else went wrong in the + * previous call to atmel_process_config_dev. + */ + fdt_for_each_subnode(subnode, gd->fdt_blob, node) { + /* Get subnode as an udevice */ + ret = uclass_find_device_by_of_offset(UCLASS_PINCONFIG, subnode, + &subconfig); + if (ret) + return ret; + + /* + * If this time the function returns an error code on a subnode + * then something is totally wrong so abort. + */ + ret = atmel_process_config_dev(dev, subconfig); + if (ret) + return ret; + + subnode_count++; + } + + /* + * If we somehow got here and we do not have any subnodes, abort. + */ + if (!subnode_count) + return -EINVAL; + + return 0; +} + const struct pinctrl_ops atmel_pinctrl_ops = { .set_state = atmel_pinctrl_set_state, }; -- 2.25.1