diff options
Diffstat (limited to 'drivers/pinctrl/pinconf-generic.c')
| -rw-r--r-- | drivers/pinctrl/pinconf-generic.c | 265 |
1 files changed, 216 insertions, 49 deletions
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index b4f7f8a458ea..366775841c63 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Core driver for the generic pin config portions of the pin control subsystem * @@ -5,23 +6,23 @@ * Written on behalf of Linaro for ST-Ericsson * * Author: Linus Walleij <linus.walleij@linaro.org> - * - * License terms: GNU General Public License (GPL) version 2 */ #define pr_fmt(fmt) "generic pinconfig core: " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> +#include <linux/array_size.h> +#include <linux/debugfs.h> #include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> #include <linux/slab.h> -#include <linux/debugfs.h> #include <linux/seq_file.h> -#include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinconf.h> + #include <linux/pinctrl/pinconf-generic.h> -#include <linux/of.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> + #include "core.h" #include "pinconf.h" #include "pinctrl-utils.h" @@ -31,41 +32,47 @@ static const struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL, false), PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL, false), PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL, false), - PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL, false), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", "ohms", true), PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, - "input bias pull to pin specific state", NULL, false), - PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL, false), + "input bias pull to pin specific state", "ohms", true), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", "ohms", true), PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL, false), PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL, false), PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL, false), PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA", true), + PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH_UA, "output drive strength", "uA", true), PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec", true), PCONFDUMP(PIN_CONFIG_INPUT_ENABLE, "input enabled", NULL, false), PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL, false), + PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_UV, "input schmitt threshold", "uV", true), PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL, false), - PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode", true), + PCONFDUMP(PIN_CONFIG_MODE_LOW_POWER, "pin low power", "mode", true), PCONFDUMP(PIN_CONFIG_OUTPUT_ENABLE, "output enabled", NULL, false), - PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true), + PCONFDUMP(PIN_CONFIG_LEVEL, "pin output", "level", true), + PCONFDUMP(PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, "output impedance", "ohms", true), PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true), PCONFDUMP(PIN_CONFIG_SLEEP_HARDWARE_STATE, "sleep hardware state", NULL, false), PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true), PCONFDUMP(PIN_CONFIG_SKEW_DELAY, "skew delay", NULL, true), + PCONFDUMP(PIN_CONFIG_SKEW_DELAY_INPUT_PS, "input skew delay", "ps", true), + PCONFDUMP(PIN_CONFIG_SKEW_DELAY_OUTPUT_PS, "output skew delay", "ps", true), }; static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, struct seq_file *s, const char *gname, - unsigned pin, + unsigned int pin, const struct pin_config_item *items, int nitems, int *print_sep) { int i; for (i = 0; i < nitems; i++) { + const struct pin_config_item *item = &items[i]; unsigned long config; int ret; /* We want to check out this parameter */ - config = pinconf_to_config_packed(items[i].param, 0); + config = pinconf_to_config_packed(item->param, 0); if (gname) ret = pin_config_group_get(dev_name(pctldev->dev), gname, &config); @@ -82,15 +89,22 @@ static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, if (*print_sep) seq_puts(s, ", "); *print_sep = 1; - seq_puts(s, items[i].display); + seq_puts(s, item->display); /* Print unit if available */ - if (items[i].has_arg) { - seq_printf(s, " (%u", - pinconf_to_config_argument(config)); - if (items[i].format) - seq_printf(s, " %s)", items[i].format); + if (item->has_arg) { + u32 val = pinconf_to_config_argument(config); + + if (item->format) + seq_printf(s, " (%u %s)", val, item->format); else - seq_puts(s, ")"); + seq_printf(s, " (0x%x)", val); + + if (item->values && item->num_values) { + if (val < item->num_values) + seq_printf(s, " \"%s\"", item->values[val]); + else + seq_puts(s, " \"(unknown)\""); + } } } } @@ -100,14 +114,14 @@ static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, * @pctldev: Pincontrol device * @s: File to print to * @gname: Group name specifying pins - * @pin: Pin number specyfying pin + * @pin: Pin number specifying pin * * Print the pinconf configuration for the requested pin(s) to @s. Pins can be * specified either by pin using @pin or by group using @gname. Only one needs * to be specified the other can be NULL/0. */ void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, - const char *gname, unsigned pin) + const char *gname, unsigned int pin) { const struct pinconf_ops *ops = pctldev->desc->confops; int print_sep = 0; @@ -167,22 +181,27 @@ static const struct pinconf_generic_params dt_params[] = { { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 }, { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 }, { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, + { "drive-strength-microamp", PIN_CONFIG_DRIVE_STRENGTH_UA, 0 }, { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, { "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 }, { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, - { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 }, - { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 }, + { "input-schmitt-microvolts", PIN_CONFIG_INPUT_SCHMITT_UV, 0 }, + { "low-power-disable", PIN_CONFIG_MODE_LOW_POWER, 0 }, + { "low-power-enable", PIN_CONFIG_MODE_LOW_POWER, 1 }, { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 }, { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 }, - { "output-high", PIN_CONFIG_OUTPUT, 1, }, - { "output-low", PIN_CONFIG_OUTPUT, 0, }, + { "output-high", PIN_CONFIG_LEVEL, 1, }, + { "output-impedance-ohms", PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, 0 }, + { "output-low", PIN_CONFIG_LEVEL, 0, }, { "power-source", PIN_CONFIG_POWER_SOURCE, 0 }, { "sleep-hardware-state", PIN_CONFIG_SLEEP_HARDWARE_STATE, 0 }, { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 }, { "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 }, + { "skew-delay-input-ps", PIN_CONFIG_SKEW_DELAY_INPUT_PS, 0 }, + { "skew-delay-output-ps", PIN_CONFIG_SKEW_DELAY_OUTPUT_PS, 0 }, }; /** @@ -198,10 +217,10 @@ static const struct pinconf_generic_params dt_params[] = { * @ncfg. @ncfg is updated to reflect the number of entries after parsing. @cfg * needs to have enough memory allocated to hold all possible entries. */ -static void parse_dt_cfg(struct device_node *np, - const struct pinconf_generic_params *params, - unsigned int count, unsigned long *cfg, - unsigned int *ncfg) +static int parse_dt_cfg(struct device_node *np, + const struct pinconf_generic_params *params, + unsigned int count, unsigned long *cfg, + unsigned int *ncfg) { int i; @@ -210,7 +229,19 @@ static void parse_dt_cfg(struct device_node *np, int ret; const struct pinconf_generic_params *par = ¶ms[i]; - ret = of_property_read_u32(np, par->property, &val); + if (par->values && par->num_values) { + ret = fwnode_property_match_property_string(of_fwnode_handle(np), + par->property, + par->values, par->num_values); + if (ret == -ENOENT) + return ret; + if (ret >= 0) { + val = ret; + ret = 0; + } + } else { + ret = of_property_read_u32(np, par->property, &val); + } /* property not found */ if (ret == -EINVAL) @@ -224,15 +255,79 @@ static void parse_dt_cfg(struct device_node *np, cfg[*ncfg] = pinconf_to_config_packed(par->param, val); (*ncfg)++; } + + return 0; } /** + * pinconf_generic_parse_dt_pinmux() + * parse the pinmux properties into generic pin mux values. + * @np: node containing the pinmux properties + * @dev: pincontrol core device + * @pid: array with pin identity entries + * @pmux: array with pin mux value entries + * @npins: number of pins + * + * pinmux property: mux value [0,7]bits and pin identity [8,31]bits. + */ +int pinconf_generic_parse_dt_pinmux(struct device_node *np, struct device *dev, + unsigned int **pid, unsigned int **pmux, + unsigned int *npins) +{ + unsigned int *pid_t; + unsigned int *pmux_t; + struct property *prop; + unsigned int npins_t, i; + u32 value; + int ret; + + prop = of_find_property(np, "pinmux", NULL); + if (!prop) { + dev_info(dev, "Missing pinmux property\n"); + return -ENOENT; + } + + if (!pid || !pmux || !npins) { + dev_err(dev, "parameters error\n"); + return -EINVAL; + } + + npins_t = prop->length / sizeof(u32); + pid_t = devm_kcalloc(dev, npins_t, sizeof(*pid_t), GFP_KERNEL); + pmux_t = devm_kcalloc(dev, npins_t, sizeof(*pmux_t), GFP_KERNEL); + if (!pid_t || !pmux_t) { + dev_err(dev, "kalloc memory fail\n"); + return -ENOMEM; + } + for (i = 0; i < npins_t; i++) { + ret = of_property_read_u32_index(np, "pinmux", i, &value); + if (ret) { + dev_err(dev, "get pinmux value fail\n"); + goto exit; + } + pmux_t[i] = value & 0xff; + pid_t[i] = (value >> 8) & 0xffffff; + } + *pid = pid_t; + *pmux = pmux_t; + *npins = npins_t; + + return 0; +exit: + devm_kfree(dev, pid_t); + devm_kfree(dev, pmux_t); + return ret; +} +EXPORT_SYMBOL_GPL(pinconf_generic_parse_dt_pinmux); + +/** * pinconf_generic_parse_dt_config() * parse the config properties into generic pinconfig values. * @np: node containing the pinconfig properties + * @pctldev: pincontrol device * @configs: array with nconfigs entries containing the generic pinconf values * must be freed when no longer necessary. - * @nconfigs: umber of configurations + * @nconfigs: number of configurations */ int pinconf_generic_parse_dt_config(struct device_node *np, struct pinctrl_dev *pctldev, @@ -254,13 +349,16 @@ int pinconf_generic_parse_dt_config(struct device_node *np, if (!cfg) return -ENOMEM; - parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); + ret = parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); + if (ret) + return ret; if (pctldev && pctldev->desc->num_custom_params && - pctldev->desc->custom_params) - parse_dt_cfg(np, pctldev->desc->custom_params, - pctldev->desc->num_custom_params, cfg, &ncfg); - - ret = 0; + pctldev->desc->custom_params) { + ret = parse_dt_cfg(np, pctldev->desc->custom_params, + pctldev->desc->num_custom_params, cfg, &ncfg); + if (ret) + return ret; + } /* no configs found at all */ if (ncfg == 0) { @@ -285,18 +383,88 @@ out: kfree(cfg); return ret; } +EXPORT_SYMBOL_GPL(pinconf_generic_parse_dt_config); + +int pinconf_generic_dt_node_to_map_pinmux(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct device *dev = pctldev->dev; + struct device_node *pnode; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + struct property *prop; + unsigned int reserved_maps; + int reserve; + int ret; + + prop = of_find_property(np, "pinmux", NULL); + if (!prop) { + dev_info(dev, "Missing pinmux property\n"); + return -ENOENT; + } + + pnode = of_get_parent(np); + if (!pnode) { + dev_info(dev, "Missing function node\n"); + return -EINVAL; + } + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, + &num_configs); + if (ret < 0) { + dev_err(dev, "%pOF: could not parse node property\n", np); + return ret; + } + + reserve = 1; + if (num_configs) + reserve++; + + ret = pinctrl_utils_reserve_map(pctldev, map, &reserved_maps, + num_maps, reserve); + if (ret < 0) + goto exit; + + ret = pinctrl_utils_add_map_mux(pctldev, map, + &reserved_maps, num_maps, np->name, + pnode->name); + if (ret < 0) + goto exit; + + if (num_configs) { + ret = pinctrl_utils_add_map_configs(pctldev, map, &reserved_maps, + num_maps, np->name, configs, + num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); + if (ret < 0) + goto exit; + } + +exit: + kfree(configs); + if (ret) + pinctrl_utils_free_map(pctldev, *map, *num_maps); + + return ret; +} +EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map_pinmux); int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, - unsigned *reserved_maps, unsigned *num_maps, + unsigned int *reserved_maps, unsigned int *num_maps, enum pinctrl_map_type type) { int ret; const char *function; struct device *dev = pctldev->dev; unsigned long *configs = NULL; - unsigned num_configs = 0; - unsigned reserve, strings_count; + unsigned int num_configs = 0; + unsigned int reserve, strings_count; struct property *prop; const char *group; const char *subnode_target_type = "pins"; @@ -372,10 +540,9 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, - unsigned *num_maps, enum pinctrl_map_type type) + unsigned int *num_maps, enum pinctrl_map_type type) { - unsigned reserved_maps; - struct device_node *np; + unsigned int reserved_maps; int ret; reserved_maps = 0; @@ -387,7 +554,7 @@ int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, if (ret < 0) goto exit; - for_each_available_child_of_node(np_config, np) { + for_each_available_child_of_node_scoped(np_config, np) { ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, &reserved_maps, num_maps, type); if (ret < 0) @@ -403,7 +570,7 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); void pinconf_generic_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, - unsigned num_maps) + unsigned int num_maps) { pinctrl_utils_free_map(pctldev, map, num_maps); } |
