From: Rafał Miłecki <rafal@xxxxxxxxxx> DT binding allows specifying pins, groups & functions now. That allows storing them in DT instead of hardcoding in drivers. This adds support for DT as data source to recently introduced API. Signed-off-by: Rafał Miłecki <rafal@xxxxxxxxxx> --- V2: Update pinctrl_generic_dt_load_pins() to support new binding --- drivers/pinctrl/core.c | 6 ++ drivers/pinctrl/devicetree.c | 131 +++++++++++++++++++++++++++++++++++ drivers/pinctrl/devicetree.h | 29 ++++++++ drivers/pinctrl/pinmux.c | 4 ++ 4 files changed, 170 insertions(+) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 53b3e8b54a9b..4c39ca338896 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -517,6 +517,9 @@ EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); int pinctrl_generic_load_pins(struct pinctrl_desc *pctldesc, struct device *dev) { + if (dev->of_node) + return pinctrl_generic_dt_load_pins(pctldesc, dev); + return -ENOENT; } EXPORT_SYMBOL_GPL(pinctrl_generic_load_pins); @@ -525,6 +528,9 @@ EXPORT_SYMBOL_GPL(pinctrl_generic_load_pins); int pinctrl_generic_load_groups(struct pinctrl_dev *pctldev) { + if (pctldev->dev->of_node) + return pinctrl_generic_load_dt_groups(pctldev); + return -ENOENT; } EXPORT_SYMBOL_GPL(pinctrl_generic_load_groups); diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 3fb238714718..5e511e61449a 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -12,6 +12,7 @@ #include "core.h" #include "devicetree.h" +#include "pinmux.h" /** * struct pinctrl_dt_map - mapping table chunk parsed from device tree @@ -27,6 +28,136 @@ struct pinctrl_dt_map { unsigned num_maps; }; +int pinctrl_generic_dt_load_pins(struct pinctrl_desc *pctldesc, + struct device *dev) +{ + struct pinctrl_pin_desc *descs; + struct device_node *pins; + struct device_node *np; + int err = 0; + int i = 0; + + pins = of_get_child_by_name(dev->of_node, "pins"); + if (!pins) { + dev_err(dev, "failed to find \"pins\" DT node\n"); + err = -ENOENT; + goto err_out; + } + + pctldesc->npins = of_get_available_child_count(pins); + + descs = devm_kcalloc(dev, pctldesc->npins, sizeof(*descs), GFP_KERNEL); + if (!descs) { + err = -ENOMEM; + goto err_put_node; + } + + for_each_available_child_of_node(pins, np) { + descs[i].name = np->name; + + if (of_property_read_u32(np, "number", &descs[i].number)) { + dev_err(dev, "missing \"number\" property in %pOF\n", np); + err = -ENOENT; + goto err_put_node; + } + + i++; + } + + pctldesc->pins = descs; + +err_put_node: + of_node_put(pins); +err_out: + return err; +} + +#ifdef CONFIG_GENERIC_PINCTRL_GROUPS + +int pinctrl_generic_load_dt_groups(struct pinctrl_dev *pctldev) +{ + struct device *dev = pctldev->dev; + struct device_node *groups; + struct device_node *np; + int err = 0; + + groups = of_get_child_by_name(dev->of_node, "groups"); + if (!groups) { + dev_err(dev, "failed to find \"groups\" DT node\n"); + err = -ENOENT; + goto err_out; + } + + for_each_available_child_of_node(groups, np) { + int num_pins; + u32 *pins; + + num_pins = of_property_count_u32_elems(np, "pins"); + pins = devm_kmalloc_array(dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) { + err = -ENOMEM; + goto err_put_node; + } + + if (of_property_read_u32_array(np, "pins", pins, num_pins)) { + err = -EIO; + goto err_put_node; + } + + pinctrl_generic_add_group(pctldev, np->name, pins, num_pins, np); + } + +err_put_node: + of_node_put(groups); +err_out: + return err; +} + +#endif + +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS +int pinmux_generic_load_dt_functions(struct pinctrl_dev *pctldev) +{ + struct device *dev = pctldev->dev; + struct device_node *functions; + struct device_node *np; + int err = 0; + + functions = of_get_child_by_name(dev->of_node, "functions"); + if (!functions) { + dev_err(dev, "failed to find \"functions\" DT node\n"); + err = -ENOENT; + goto err_out; + } + + for_each_available_child_of_node(functions, np) { + int num_groups = of_count_phandle_with_args(np, "groups", NULL); + struct of_phandle_iterator it; + const char **groups; + int ret; + int i; + + groups = devm_kmalloc_array(dev, num_groups, sizeof(*groups), GFP_KERNEL); + if (!groups) { + err = -ENOMEM; + goto err_put_node; + } + + i = 0; + of_for_each_phandle(&it, ret, np, "groups", NULL, 0) { + groups[i++] = it.node->name; + } + + pinmux_generic_add_function(pctldev, np->name, groups, num_groups, np); + } + +err_put_node: + of_node_put(functions); +err_out: + return err; +} +#endif + static void dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps) { diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h index efa80779de4f..156f13896c39 100644 --- a/drivers/pinctrl/devicetree.h +++ b/drivers/pinctrl/devicetree.h @@ -9,6 +9,15 @@ struct of_phandle_args; #ifdef CONFIG_OF +int pinctrl_generic_dt_load_pins(struct pinctrl_desc *pctldesc, + struct device *dev); +#ifdef CONFIG_GENERIC_PINCTRL_GROUPS +int pinctrl_generic_load_dt_groups(struct pinctrl_dev *pctldev); +#endif +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS +int pinmux_generic_load_dt_functions(struct pinctrl_dev *pctldev); +#endif + void pinctrl_dt_free_maps(struct pinctrl *p); int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev); @@ -21,6 +30,26 @@ int pinctrl_parse_index_with_args(const struct device_node *np, #else +static inline int pinctrl_generic_dt_load_pins(struct pinctrl_desc *pctldesc, + struct device *dev) +{ + return -EOPNOTSUPP; +} + +#ifdef CONFIG_GENERIC_PINCTRL_GROUPS +static inline int pinctrl_generic_load_dt_groups(struct pinctrl_dev *pctldev) +{ + return -EOPNOTSUPP; +} +#endif + +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS +static inline int pinmux_generic_load_dt_functions(struct pinctrl_dev *pctldev) +{ + return -EOPNOTSUPP; +} +#endif + static inline int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev) { diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index ef7d2cbf0946..36a1d1af4a20 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -27,6 +27,7 @@ #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinmux.h> #include "core.h" +#include "devicetree.h" #include "pinmux.h" int pinmux_check_ops(struct pinctrl_dev *pctldev) @@ -790,6 +791,9 @@ void pinmux_init_device_debugfs(struct dentry *devroot, int pinmux_generic_load_functions(struct pinctrl_dev *pctldev) { + if (pctldev->dev->of_node) + return pinmux_generic_load_dt_functions(pctldev); + return -ENOENT; } EXPORT_SYMBOL_GPL(pinmux_generic_load_functions); -- 2.31.1