162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Core driver for the generic pin config portions of the pin control subsystem 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 ST-Ericsson SA 662306a36Sopenharmony_ci * Written on behalf of Linaro for ST-Ericsson 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "generic pinconfig core: " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/debugfs.h> 1962306a36Sopenharmony_ci#include <linux/seq_file.h> 2062306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 2162306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include "core.h" 2562306a36Sopenharmony_ci#include "pinconf.h" 2662306a36Sopenharmony_ci#include "pinctrl-utils.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 2962306a36Sopenharmony_cistatic const struct pin_config_item conf_items[] = { 3062306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL, false), 3162306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL, false), 3262306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL, false), 3362306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", "ohms", true), 3462306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 3562306a36Sopenharmony_ci "input bias pull to pin specific state", "ohms", true), 3662306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", "ohms", true), 3762306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL, false), 3862306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL, false), 3962306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL, false), 4062306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA", true), 4162306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH_UA, "output drive strength", "uA", true), 4262306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec", true), 4362306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_INPUT_ENABLE, "input enabled", NULL, false), 4462306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL, false), 4562306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL, false), 4662306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_MODE_LOW_POWER, "pin low power", "mode", true), 4762306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_OUTPUT_ENABLE, "output enabled", NULL, false), 4862306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true), 4962306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, "output impedance", "ohms", true), 5062306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true), 5162306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_SLEEP_HARDWARE_STATE, "sleep hardware state", NULL, false), 5262306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true), 5362306a36Sopenharmony_ci PCONFDUMP(PIN_CONFIG_SKEW_DELAY, "skew delay", NULL, true), 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, 5762306a36Sopenharmony_ci struct seq_file *s, const char *gname, 5862306a36Sopenharmony_ci unsigned pin, 5962306a36Sopenharmony_ci const struct pin_config_item *items, 6062306a36Sopenharmony_ci int nitems, int *print_sep) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci int i; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci for (i = 0; i < nitems; i++) { 6562306a36Sopenharmony_ci unsigned long config; 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* We want to check out this parameter */ 6962306a36Sopenharmony_ci config = pinconf_to_config_packed(items[i].param, 0); 7062306a36Sopenharmony_ci if (gname) 7162306a36Sopenharmony_ci ret = pin_config_group_get(dev_name(pctldev->dev), 7262306a36Sopenharmony_ci gname, &config); 7362306a36Sopenharmony_ci else 7462306a36Sopenharmony_ci ret = pin_config_get_for_pin(pctldev, pin, &config); 7562306a36Sopenharmony_ci /* These are legal errors */ 7662306a36Sopenharmony_ci if (ret == -EINVAL || ret == -ENOTSUPP) 7762306a36Sopenharmony_ci continue; 7862306a36Sopenharmony_ci if (ret) { 7962306a36Sopenharmony_ci seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); 8062306a36Sopenharmony_ci continue; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci /* comma between multiple configs */ 8362306a36Sopenharmony_ci if (*print_sep) 8462306a36Sopenharmony_ci seq_puts(s, ", "); 8562306a36Sopenharmony_ci *print_sep = 1; 8662306a36Sopenharmony_ci seq_puts(s, items[i].display); 8762306a36Sopenharmony_ci /* Print unit if available */ 8862306a36Sopenharmony_ci if (items[i].has_arg) { 8962306a36Sopenharmony_ci seq_printf(s, " (%u", 9062306a36Sopenharmony_ci pinconf_to_config_argument(config)); 9162306a36Sopenharmony_ci if (items[i].format) 9262306a36Sopenharmony_ci seq_printf(s, " %s)", items[i].format); 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci seq_puts(s, ")"); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/** 10062306a36Sopenharmony_ci * pinconf_generic_dump_pins - Print information about pin or group of pins 10162306a36Sopenharmony_ci * @pctldev: Pincontrol device 10262306a36Sopenharmony_ci * @s: File to print to 10362306a36Sopenharmony_ci * @gname: Group name specifying pins 10462306a36Sopenharmony_ci * @pin: Pin number specyfying pin 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Print the pinconf configuration for the requested pin(s) to @s. Pins can be 10762306a36Sopenharmony_ci * specified either by pin using @pin or by group using @gname. Only one needs 10862306a36Sopenharmony_ci * to be specified the other can be NULL/0. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_civoid pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, 11162306a36Sopenharmony_ci const char *gname, unsigned pin) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci const struct pinconf_ops *ops = pctldev->desc->confops; 11462306a36Sopenharmony_ci int print_sep = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!ops->is_generic) 11762306a36Sopenharmony_ci return; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* generic parameters */ 12062306a36Sopenharmony_ci pinconf_generic_dump_one(pctldev, s, gname, pin, conf_items, 12162306a36Sopenharmony_ci ARRAY_SIZE(conf_items), &print_sep); 12262306a36Sopenharmony_ci /* driver-specific parameters */ 12362306a36Sopenharmony_ci if (pctldev->desc->num_custom_params && 12462306a36Sopenharmony_ci pctldev->desc->custom_conf_items) 12562306a36Sopenharmony_ci pinconf_generic_dump_one(pctldev, s, gname, pin, 12662306a36Sopenharmony_ci pctldev->desc->custom_conf_items, 12762306a36Sopenharmony_ci pctldev->desc->num_custom_params, 12862306a36Sopenharmony_ci &print_sep); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid pinconf_generic_dump_config(struct pinctrl_dev *pctldev, 13262306a36Sopenharmony_ci struct seq_file *s, unsigned long config) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int i; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(conf_items); i++) { 13762306a36Sopenharmony_ci if (pinconf_to_config_param(config) != conf_items[i].param) 13862306a36Sopenharmony_ci continue; 13962306a36Sopenharmony_ci seq_printf(s, "%s: 0x%x", conf_items[i].display, 14062306a36Sopenharmony_ci pinconf_to_config_argument(config)); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!pctldev->desc->num_custom_params || 14462306a36Sopenharmony_ci !pctldev->desc->custom_conf_items) 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < pctldev->desc->num_custom_params; i++) { 14862306a36Sopenharmony_ci if (pinconf_to_config_param(config) != 14962306a36Sopenharmony_ci pctldev->desc->custom_conf_items[i].param) 15062306a36Sopenharmony_ci continue; 15162306a36Sopenharmony_ci seq_printf(s, "%s: 0x%x", 15262306a36Sopenharmony_ci pctldev->desc->custom_conf_items[i].display, 15362306a36Sopenharmony_ci pinconf_to_config_argument(config)); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pinconf_generic_dump_config); 15762306a36Sopenharmony_ci#endif 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#ifdef CONFIG_OF 16062306a36Sopenharmony_cistatic const struct pinconf_generic_params dt_params[] = { 16162306a36Sopenharmony_ci { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, 16262306a36Sopenharmony_ci { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, 16362306a36Sopenharmony_ci { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, 16462306a36Sopenharmony_ci { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, 16562306a36Sopenharmony_ci { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, 16662306a36Sopenharmony_ci { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, 16762306a36Sopenharmony_ci { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, 16862306a36Sopenharmony_ci { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 }, 16962306a36Sopenharmony_ci { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 }, 17062306a36Sopenharmony_ci { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, 17162306a36Sopenharmony_ci { "drive-strength-microamp", PIN_CONFIG_DRIVE_STRENGTH_UA, 0 }, 17262306a36Sopenharmony_ci { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, 17362306a36Sopenharmony_ci { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, 17462306a36Sopenharmony_ci { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, 17562306a36Sopenharmony_ci { "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 }, 17662306a36Sopenharmony_ci { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, 17762306a36Sopenharmony_ci { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, 17862306a36Sopenharmony_ci { "low-power-disable", PIN_CONFIG_MODE_LOW_POWER, 0 }, 17962306a36Sopenharmony_ci { "low-power-enable", PIN_CONFIG_MODE_LOW_POWER, 1 }, 18062306a36Sopenharmony_ci { "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 }, 18162306a36Sopenharmony_ci { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 }, 18262306a36Sopenharmony_ci { "output-high", PIN_CONFIG_OUTPUT, 1, }, 18362306a36Sopenharmony_ci { "output-impedance-ohms", PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, 0 }, 18462306a36Sopenharmony_ci { "output-low", PIN_CONFIG_OUTPUT, 0, }, 18562306a36Sopenharmony_ci { "power-source", PIN_CONFIG_POWER_SOURCE, 0 }, 18662306a36Sopenharmony_ci { "sleep-hardware-state", PIN_CONFIG_SLEEP_HARDWARE_STATE, 0 }, 18762306a36Sopenharmony_ci { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 }, 18862306a36Sopenharmony_ci { "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 }, 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * parse_dt_cfg() - Parse DT pinconf parameters 19362306a36Sopenharmony_ci * @np: DT node 19462306a36Sopenharmony_ci * @params: Array of describing generic parameters 19562306a36Sopenharmony_ci * @count: Number of entries in @params 19662306a36Sopenharmony_ci * @cfg: Array of parsed config options 19762306a36Sopenharmony_ci * @ncfg: Number of entries in @cfg 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Parse the config options described in @params from @np and puts the result 20062306a36Sopenharmony_ci * in @cfg. @cfg does not need to be empty, entries are added beginning at 20162306a36Sopenharmony_ci * @ncfg. @ncfg is updated to reflect the number of entries after parsing. @cfg 20262306a36Sopenharmony_ci * needs to have enough memory allocated to hold all possible entries. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_cistatic void parse_dt_cfg(struct device_node *np, 20562306a36Sopenharmony_ci const struct pinconf_generic_params *params, 20662306a36Sopenharmony_ci unsigned int count, unsigned long *cfg, 20762306a36Sopenharmony_ci unsigned int *ncfg) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int i; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for (i = 0; i < count; i++) { 21262306a36Sopenharmony_ci u32 val; 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci const struct pinconf_generic_params *par = ¶ms[i]; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = of_property_read_u32(np, par->property, &val); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* property not found */ 21962306a36Sopenharmony_ci if (ret == -EINVAL) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* use default value, when no value is specified */ 22362306a36Sopenharmony_ci if (ret) 22462306a36Sopenharmony_ci val = par->default_value; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pr_debug("found %s with value %u\n", par->property, val); 22762306a36Sopenharmony_ci cfg[*ncfg] = pinconf_to_config_packed(par->param, val); 22862306a36Sopenharmony_ci (*ncfg)++; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * pinconf_generic_parse_dt_config() 23462306a36Sopenharmony_ci * parse the config properties into generic pinconfig values. 23562306a36Sopenharmony_ci * @np: node containing the pinconfig properties 23662306a36Sopenharmony_ci * @pctldev: pincontrol device 23762306a36Sopenharmony_ci * @configs: array with nconfigs entries containing the generic pinconf values 23862306a36Sopenharmony_ci * must be freed when no longer necessary. 23962306a36Sopenharmony_ci * @nconfigs: number of configurations 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ciint pinconf_generic_parse_dt_config(struct device_node *np, 24262306a36Sopenharmony_ci struct pinctrl_dev *pctldev, 24362306a36Sopenharmony_ci unsigned long **configs, 24462306a36Sopenharmony_ci unsigned int *nconfigs) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci unsigned long *cfg; 24762306a36Sopenharmony_ci unsigned int max_cfg, ncfg = 0; 24862306a36Sopenharmony_ci int ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!np) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* allocate a temporary array big enough to hold one of each option */ 25462306a36Sopenharmony_ci max_cfg = ARRAY_SIZE(dt_params); 25562306a36Sopenharmony_ci if (pctldev) 25662306a36Sopenharmony_ci max_cfg += pctldev->desc->num_custom_params; 25762306a36Sopenharmony_ci cfg = kcalloc(max_cfg, sizeof(*cfg), GFP_KERNEL); 25862306a36Sopenharmony_ci if (!cfg) 25962306a36Sopenharmony_ci return -ENOMEM; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); 26262306a36Sopenharmony_ci if (pctldev && pctldev->desc->num_custom_params && 26362306a36Sopenharmony_ci pctldev->desc->custom_params) 26462306a36Sopenharmony_ci parse_dt_cfg(np, pctldev->desc->custom_params, 26562306a36Sopenharmony_ci pctldev->desc->num_custom_params, cfg, &ncfg); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* no configs found at all */ 27062306a36Sopenharmony_ci if (ncfg == 0) { 27162306a36Sopenharmony_ci *configs = NULL; 27262306a36Sopenharmony_ci *nconfigs = 0; 27362306a36Sopenharmony_ci goto out; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * Now limit the number of configs to the real number of 27862306a36Sopenharmony_ci * found properties. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci *configs = kmemdup(cfg, ncfg * sizeof(unsigned long), GFP_KERNEL); 28162306a36Sopenharmony_ci if (!*configs) { 28262306a36Sopenharmony_ci ret = -ENOMEM; 28362306a36Sopenharmony_ci goto out; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci *nconfigs = ncfg; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciout: 28962306a36Sopenharmony_ci kfree(cfg); 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pinconf_generic_parse_dt_config); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciint pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, 29562306a36Sopenharmony_ci struct device_node *np, struct pinctrl_map **map, 29662306a36Sopenharmony_ci unsigned *reserved_maps, unsigned *num_maps, 29762306a36Sopenharmony_ci enum pinctrl_map_type type) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci int ret; 30062306a36Sopenharmony_ci const char *function; 30162306a36Sopenharmony_ci struct device *dev = pctldev->dev; 30262306a36Sopenharmony_ci unsigned long *configs = NULL; 30362306a36Sopenharmony_ci unsigned num_configs = 0; 30462306a36Sopenharmony_ci unsigned reserve, strings_count; 30562306a36Sopenharmony_ci struct property *prop; 30662306a36Sopenharmony_ci const char *group; 30762306a36Sopenharmony_ci const char *subnode_target_type = "pins"; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = of_property_count_strings(np, "pins"); 31062306a36Sopenharmony_ci if (ret < 0) { 31162306a36Sopenharmony_ci ret = of_property_count_strings(np, "groups"); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci /* skip this node; may contain config child nodes */ 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci if (type == PIN_MAP_TYPE_INVALID) 31662306a36Sopenharmony_ci type = PIN_MAP_TYPE_CONFIGS_GROUP; 31762306a36Sopenharmony_ci subnode_target_type = "groups"; 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci if (type == PIN_MAP_TYPE_INVALID) 32062306a36Sopenharmony_ci type = PIN_MAP_TYPE_CONFIGS_PIN; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci strings_count = ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = of_property_read_string(np, "function", &function); 32562306a36Sopenharmony_ci if (ret < 0) { 32662306a36Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 32762306a36Sopenharmony_ci if (ret != -EINVAL) 32862306a36Sopenharmony_ci dev_err(dev, "%pOF: could not parse property function\n", 32962306a36Sopenharmony_ci np); 33062306a36Sopenharmony_ci function = NULL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, 33462306a36Sopenharmony_ci &num_configs); 33562306a36Sopenharmony_ci if (ret < 0) { 33662306a36Sopenharmony_ci dev_err(dev, "%pOF: could not parse node property\n", np); 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci reserve = 0; 34162306a36Sopenharmony_ci if (function != NULL) 34262306a36Sopenharmony_ci reserve++; 34362306a36Sopenharmony_ci if (num_configs) 34462306a36Sopenharmony_ci reserve++; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci reserve *= strings_count; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, 34962306a36Sopenharmony_ci num_maps, reserve); 35062306a36Sopenharmony_ci if (ret < 0) 35162306a36Sopenharmony_ci goto exit; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci of_property_for_each_string(np, subnode_target_type, prop, group) { 35462306a36Sopenharmony_ci if (function) { 35562306a36Sopenharmony_ci ret = pinctrl_utils_add_map_mux(pctldev, map, 35662306a36Sopenharmony_ci reserved_maps, num_maps, group, 35762306a36Sopenharmony_ci function); 35862306a36Sopenharmony_ci if (ret < 0) 35962306a36Sopenharmony_ci goto exit; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (num_configs) { 36362306a36Sopenharmony_ci ret = pinctrl_utils_add_map_configs(pctldev, map, 36462306a36Sopenharmony_ci reserved_maps, num_maps, group, configs, 36562306a36Sopenharmony_ci num_configs, type); 36662306a36Sopenharmony_ci if (ret < 0) 36762306a36Sopenharmony_ci goto exit; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci ret = 0; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciexit: 37362306a36Sopenharmony_ci kfree(configs); 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ciint pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, 37962306a36Sopenharmony_ci struct device_node *np_config, struct pinctrl_map **map, 38062306a36Sopenharmony_ci unsigned *num_maps, enum pinctrl_map_type type) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci unsigned reserved_maps; 38362306a36Sopenharmony_ci struct device_node *np; 38462306a36Sopenharmony_ci int ret; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci reserved_maps = 0; 38762306a36Sopenharmony_ci *map = NULL; 38862306a36Sopenharmony_ci *num_maps = 0; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ret = pinconf_generic_dt_subnode_to_map(pctldev, np_config, map, 39162306a36Sopenharmony_ci &reserved_maps, num_maps, type); 39262306a36Sopenharmony_ci if (ret < 0) 39362306a36Sopenharmony_ci goto exit; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci for_each_available_child_of_node(np_config, np) { 39662306a36Sopenharmony_ci ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, 39762306a36Sopenharmony_ci &reserved_maps, num_maps, type); 39862306a36Sopenharmony_ci if (ret < 0) { 39962306a36Sopenharmony_ci of_node_put(np); 40062306a36Sopenharmony_ci goto exit; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciexit: 40662306a36Sopenharmony_ci pinctrl_utils_free_map(pctldev, *map, *num_maps); 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_civoid pinconf_generic_dt_free_map(struct pinctrl_dev *pctldev, 41262306a36Sopenharmony_ci struct pinctrl_map *map, 41362306a36Sopenharmony_ci unsigned num_maps) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci pinctrl_utils_free_map(pctldev, map, num_maps); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pinconf_generic_dt_free_map); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci#endif 420