--- zzzz-none-000/linux-3.10.107/drivers/pinctrl/pinconf-generic.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/pinctrl/pinconf-generic.c 2021-02-04 17:41:59.000000000 +0000 @@ -21,52 +21,53 @@ #include #include #include +#include #include "core.h" #include "pinconf.h" +#include "pinctrl-utils.h" #ifdef CONFIG_DEBUG_FS - -struct pin_config_item { - const enum pin_config_param param; - const char * const display; - const char * const format; +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_PIN_DEFAULT, + "input bias pull to pin specific state", NULL, false), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL, false), + 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_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_ENABLE, "input schmitt enabled", NULL, false), + PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode", true), + PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true), + PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true), + PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true), }; -#define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c } - -static struct pin_config_item conf_items[] = { - PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL), - PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL), - PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL), - PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL), - PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL), - PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL), - PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL), - PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL), - PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL), - PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"), - PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"), - PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL), - PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"), - PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level"), -}; - -void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned pin) +static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, + struct seq_file *s, const char *gname, + unsigned pin, + const struct pin_config_item *items, + int nitems) { - const struct pinconf_ops *ops = pctldev->desc->confops; int i; - if (!ops->is_generic) - return; - - for (i = 0; i < ARRAY_SIZE(conf_items); i++) { + for (i = 0; i < nitems; i++) { unsigned long config; int ret; /* We want to check out this parameter */ - config = pinconf_to_config_packed(conf_items[i].param, 0); - ret = pin_config_get_for_pin(pctldev, pin, &config); + config = pinconf_to_config_packed(items[i].param, 0); + if (gname) + ret = pin_config_group_get(dev_name(pctldev->dev), + gname, &config); + else + ret = pin_config_get_for_pin(pctldev, pin, &config); /* These are legal errors */ if (ret == -EINVAL || ret == -ENOTSUPP) continue; @@ -76,49 +77,47 @@ } /* Space between multiple configs */ seq_puts(s, " "); - seq_puts(s, conf_items[i].display); + seq_puts(s, items[i].display); /* Print unit if available */ - if (conf_items[i].format && - pinconf_to_config_argument(config) != 0) - seq_printf(s, " (%u %s)", - pinconf_to_config_argument(config), - conf_items[i].format); + 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); + else + seq_puts(s, ")"); + } } } -void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, - struct seq_file *s, const char *gname) +/** + * pinconf_generic_dump_pins - Print information about pin or group of pins + * @pctldev: Pincontrol device + * @s: File to print to + * @gname: Group name specifying pins + * @pin: Pin number specyfying 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 struct pinconf_ops *ops = pctldev->desc->confops; - int i; if (!ops->is_generic) return; - for (i = 0; i < ARRAY_SIZE(conf_items); i++) { - unsigned long config; - int ret; - - /* We want to check out this parameter */ - config = pinconf_to_config_packed(conf_items[i].param, 0); - ret = pin_config_group_get(dev_name(pctldev->dev), gname, - &config); - /* These are legal errors */ - if (ret == -EINVAL || ret == -ENOTSUPP) - continue; - if (ret) { - seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); - continue; - } - /* Space between multiple configs */ - seq_puts(s, " "); - seq_puts(s, conf_items[i].display); - /* Print unit if available */ - if (conf_items[i].format && config != 0) - seq_printf(s, " (%u %s)", - pinconf_to_config_argument(config), - conf_items[i].format); - } + /* generic parameters */ + pinconf_generic_dump_one(pctldev, s, gname, pin, conf_items, + ARRAY_SIZE(conf_items)); + /* driver-specific parameters */ + if (pctldev->desc->num_custom_params && + pctldev->desc->custom_conf_items) + pinconf_generic_dump_one(pctldev, s, gname, pin, + pctldev->desc->custom_conf_items, + pctldev->desc->num_custom_params); } void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, @@ -132,6 +131,268 @@ seq_printf(s, "%s: 0x%x", conf_items[i].display, pinconf_to_config_argument(config)); } + + if (!pctldev->desc->num_custom_params || + !pctldev->desc->custom_conf_items) + return; + + for (i = 0; i < pctldev->desc->num_custom_params; i++) { + if (pinconf_to_config_param(config) != + pctldev->desc->custom_conf_items[i].param) + continue; + seq_printf(s, "%s: 0x%x", + pctldev->desc->custom_conf_items[i].display, + pinconf_to_config_argument(config)); + } } EXPORT_SYMBOL_GPL(pinconf_generic_dump_config); #endif + +#ifdef CONFIG_OF +static const struct pinconf_generic_params dt_params[] = { + { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, + { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, + { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, + { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, + { "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 }, + { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, + { "drive-cap", PIN_CONFIG_DRIVE_CAP, 0 }, + { "drive-type", PIN_CONFIG_DRIVE_TYPE, 0 }, + { "pull-res", PIN_CONFIG_PULL_RES, 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 }, + { "vm-enable", PIN_CONFIG_VM, 1 }, + { "vm-disable", PIN_CONFIG_VM, 0 }, + { "output-high", PIN_CONFIG_OUTPUT, 1, }, + { "output-low", PIN_CONFIG_OUTPUT, 0, }, + { "power-source", PIN_CONFIG_POWER_SOURCE, 0 }, + { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 }, +}; + +/** + * parse_dt_cfg() - Parse DT pinconf parameters + * @np: DT node + * @params: Array of describing generic parameters + * @count: Number of entries in @params + * @cfg: Array of parsed config options + * @ncfg: Number of entries in @cfg + * + * Parse the config options described in @params from @np and puts the result + * in @cfg. @cfg does not need to be empty, entries are added beggining at + * @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) +{ + int i; + + for (i = 0; i < count; i++) { + u32 val; + int ret; + const struct pinconf_generic_params *par = ¶ms[i]; + + ret = of_property_read_u32(np, par->property, &val); + + /* property not found */ + if (ret == -EINVAL) + continue; + + /* use default value, when no value is specified */ + if (ret) + val = par->default_value; + + pr_debug("found %s with value %u\n", par->property, val); + cfg[*ncfg] = pinconf_to_config_packed(par->param, val); + (*ncfg)++; + } +} + +/** + * pinconf_generic_parse_dt_config() + * parse the config properties into generic pinconfig values. + * @np: node containing the pinconfig properties + * @configs: array with nconfigs entries containing the generic pinconf values + * @nconfigs: umber of configurations + */ +int pinconf_generic_parse_dt_config(struct device_node *np, + struct pinctrl_dev *pctldev, + unsigned long **configs, + unsigned int *nconfigs) +{ + unsigned long *cfg; + unsigned int max_cfg, ncfg = 0; + int ret; + + if (!np) + return -EINVAL; + + /* allocate a temporary array big enough to hold one of each option */ + max_cfg = ARRAY_SIZE(dt_params); + if (pctldev) + max_cfg += pctldev->desc->num_custom_params; + cfg = kcalloc(max_cfg, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); + 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; + + /* no configs found at all */ + if (ncfg == 0) { + *configs = NULL; + *nconfigs = 0; + goto out; + } + + /* + * Now limit the number of configs to the real number of + * found properties. + */ + *configs = kmemdup(cfg, ncfg * sizeof(unsigned long), GFP_KERNEL); + if (!*configs) { + ret = -ENOMEM; + goto out; + } + + *nconfigs = ncfg; + +out: + kfree(cfg); + return ret; +} + +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, + 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; + struct property *prop; + const char *group; + const char *subnode_target_type = "pins"; + + ret = of_property_count_strings(np, "pins"); + if (ret < 0) { + ret = of_property_count_strings(np, "groups"); + if (ret < 0) + /* skip this node; may contain config child nodes */ + return 0; + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_GROUP; + subnode_target_type = "groups"; + } else { + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_PIN; + } + strings_count = ret; + + ret = of_property_read_string(np, "function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, "%s: could not parse property function\n", + of_node_full_name(np)); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, + &num_configs); + if (ret < 0) { + dev_err(dev, "%s: could not parse node property\n", + of_node_full_name(np)); + return ret; + } + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + + reserve *= strings_count; + + ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, + num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, subnode_target_type, prop, group) { + if (function) { + ret = pinctrl_utils_add_map_mux(pctldev, map, + reserved_maps, num_maps, group, + function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = pinctrl_utils_add_map_configs(pctldev, map, + reserved_maps, num_maps, group, configs, + num_configs, type); + if (ret < 0) + goto exit; + } + } + ret = 0; + +exit: + kfree(configs); + return ret; +} +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 reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + ret = pinconf_generic_dt_subnode_to_map(pctldev, np_config, map, + &reserved_maps, num_maps, type); + if (ret < 0) + goto exit; + + for_each_child_of_node(np_config, np) { + ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps, type); + if (ret < 0) + goto exit; + } + return 0; + +exit: + pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); + return ret; +} +EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); + +#endif