summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/pinconf-generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinconf-generic.c')
-rw-r--r--drivers/pinctrl/pinconf-generic.c276
1 files changed, 223 insertions, 53 deletions
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index fc0c230aa11f..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,39 +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);
@@ -80,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)\"");
+ }
}
}
}
@@ -98,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;
@@ -165,20 +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 },
};
/**
@@ -194,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;
@@ -206,7 +229,19 @@ static void parse_dt_cfg(struct device_node *np,
int ret;
const struct pinconf_generic_params *par = &params[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)
@@ -220,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,
@@ -250,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) {
@@ -281,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";
@@ -316,16 +488,15 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev,
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));
+ dev_err(dev, "%pOF: could not parse property function\n",
+ 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));
+ dev_err(dev, "%pOF: could not parse node property\n", np);
return ret;
}
@@ -369,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;
@@ -384,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)
@@ -400,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);
}