mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
Merge branch 'v3.10/topic/pinctrl' of git://git.linaro.org/kernel/linux-linaro-stable into linux-linaro-lsk
This commit is contained in:
@@ -126,3 +126,55 @@ device; they may be grandchildren, for example. Whether this is legal, and
|
||||
whether there is any interaction between the child and intermediate parent
|
||||
nodes, is again defined entirely by the binding for the individual pin
|
||||
controller device.
|
||||
|
||||
== Using generic pinconfig options ==
|
||||
|
||||
Generic pinconfig parameters can be used by defining a separate node containing
|
||||
the applicable parameters (and optional values), like:
|
||||
|
||||
pcfg_pull_up: pcfg_pull_up {
|
||||
bias-pull-up;
|
||||
drive-strength = <20>;
|
||||
};
|
||||
|
||||
This node should then be referenced in the appropriate pinctrl node as a phandle
|
||||
and parsed in the driver using the pinconf_generic_parse_dt_config function.
|
||||
|
||||
Supported configuration parameters are:
|
||||
|
||||
bias-disable - disable any pin bias
|
||||
bias-high-impedance - high impedance mode ("third-state", "floating")
|
||||
bias-bus-hold - latch weakly
|
||||
bias-pull-up - pull up the pin
|
||||
bias-pull-down - pull down the pin
|
||||
bias-pull-pin-default - use pin-default pull state
|
||||
drive-push-pull - drive actively high and low
|
||||
drive-open-drain - drive with open drain
|
||||
drive-open-source - drive with open source
|
||||
drive-strength - sink or source at most X mA
|
||||
input-enable - enable input on pin (no effect on output)
|
||||
input-disable - disable input on pin (no effect on output)
|
||||
input-schmitt-enable - enable schmitt-trigger mode
|
||||
input-schmitt-disable - disable schmitt-trigger mode
|
||||
input-debounce - debounce mode with debound time X
|
||||
low-power-enable - enable low power mode
|
||||
low-power-disable - disable low power mode
|
||||
output-low - set the pin to output mode with low level
|
||||
output-high - set the pin to output mode with high level
|
||||
slew-rate - set the slew rate
|
||||
|
||||
Arguments for parameters:
|
||||
|
||||
- bias-pull-up, -down and -pin-default take as optional argument 0 to disable
|
||||
the pull, on hardware supporting it the pull strength in Ohm. bias-disable
|
||||
will also disable any active pull.
|
||||
|
||||
- drive-strength takes as argument the target strength in mA.
|
||||
|
||||
- input-debounce takes the debounce time in usec as argument
|
||||
or 0 to disable debouncing
|
||||
|
||||
All parameters not listed here, do not take an argument.
|
||||
|
||||
More in-depth documentation on these parameters can be found in
|
||||
<include/linux/pinctrl/pinconfig-generic.h>
|
||||
|
||||
@@ -203,15 +203,8 @@ using a certain resistor value - pull up and pull down - so that the pin has a
|
||||
stable value when nothing is driving the rail it is connected to, or when it's
|
||||
unconnected.
|
||||
|
||||
Pin configuration can be programmed either using the explicit APIs described
|
||||
immediately below, or by adding configuration entries into the mapping table;
|
||||
see section "Board/machine configuration" below.
|
||||
|
||||
For example, a platform may do the following to pull up a pin to VDD:
|
||||
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP);
|
||||
Pin configuration can be programmed by adding configuration entries into the
|
||||
mapping table; see section "Board/machine configuration" below.
|
||||
|
||||
The format and meaning of the configuration parameter, PLATFORM_X_PULL_UP
|
||||
above, is entirely defined by the pin controller driver.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG
|
||||
|
||||
obj-$(CONFIG_PINCTRL) += core.o
|
||||
obj-$(CONFIG_PINCTRL) += core.o pinctrl-utils.o
|
||||
obj-$(CONFIG_PINMUX) += pinmux.o
|
||||
obj-$(CONFIG_PINCONF) += pinconf.o
|
||||
ifeq ($(CONFIG_OF),y)
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/of.h>
|
||||
#include "core.h"
|
||||
#include "pinconf.h"
|
||||
#include "pinctrl-utils.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
@@ -37,14 +39,19 @@ struct pin_config_item {
|
||||
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_BUS_HOLD, "input bias bus hold", 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_BIAS_PULL_PIN_DEFAULT,
|
||||
"input bias pull to pin specific state", 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_DRIVE_STRENGTH, "output drive strength", "mA"),
|
||||
PCONFDUMP(PIN_CONFIG_INPUT_ENABLE, "input enabled", 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_INPUT_DEBOUNCE, "input debounce", "usec"),
|
||||
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"),
|
||||
@@ -135,3 +142,198 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinconf_generic_dump_config);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
struct pinconf_generic_dt_params {
|
||||
const char * const property;
|
||||
enum pin_config_param param;
|
||||
u32 default_value;
|
||||
};
|
||||
|
||||
static struct pinconf_generic_dt_params dt_params[] = {
|
||||
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
|
||||
{ "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
|
||||
{ "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
|
||||
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
|
||||
{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
|
||||
{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
|
||||
{ "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
|
||||
{ "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
|
||||
{ "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
|
||||
{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
|
||||
{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
|
||||
{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
|
||||
{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
|
||||
{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
|
||||
{ "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
|
||||
{ "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
|
||||
{ "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
|
||||
{ "output-low", PIN_CONFIG_OUTPUT, 0, },
|
||||
{ "output-high", PIN_CONFIG_OUTPUT, 1, },
|
||||
{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0},
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,
|
||||
unsigned long **configs,
|
||||
unsigned int *nconfigs)
|
||||
{
|
||||
unsigned long *cfg;
|
||||
unsigned int ncfg = 0;
|
||||
int ret;
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
/* allocate a temporary array big enough to hold one of each option */
|
||||
cfg = kzalloc(sizeof(*cfg) * ARRAY_SIZE(dt_params), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
|
||||
struct pinconf_generic_dt_params *par = &dt_params[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++;
|
||||
}
|
||||
|
||||
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 = kzalloc(ncfg * sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!*configs) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
|
||||
*nconfigs = ncfg;
|
||||
|
||||
out:
|
||||
kfree(cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pinconf_generic_dt_subnode_to_map_new(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;
|
||||
struct property *prop;
|
||||
const char *group;
|
||||
|
||||
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, "could not parse property function\n");
|
||||
function = NULL;
|
||||
}
|
||||
|
||||
ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "could not parse node property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reserve = 0;
|
||||
if (function != NULL)
|
||||
reserve++;
|
||||
if (num_configs)
|
||||
reserve++;
|
||||
ret = of_property_count_strings(np, "pins");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "could not parse property pins\n");
|
||||
goto exit;
|
||||
}
|
||||
reserve *= ret;
|
||||
|
||||
ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps,
|
||||
num_maps, reserve);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
of_property_for_each_string(np, "pins", 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_new);
|
||||
|
||||
int pinconf_generic_dt_node_to_map_new(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;
|
||||
|
||||
for_each_child_of_node(np_config, np) {
|
||||
ret = pinconf_generic_dt_subnode_to_map_new(pctldev, np, map,
|
||||
&reserved_maps, num_maps, type);
|
||||
if (ret < 0) {
|
||||
pinctrl_utils_dt_free_map(pctldev, *map, *num_maps);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map_new);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -75,98 +75,6 @@ int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
|
||||
return ops->pin_config_get(pctldev, pin, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* pin_config_get() - get the configuration of a single pin parameter
|
||||
* @dev_name: name of the pin controller device for this pin
|
||||
* @name: name of the pin to get the config for
|
||||
* @config: the config pointed to by this argument will be filled in with the
|
||||
* current pin state, it can be used directly by drivers as a numeral, or
|
||||
* it can be dereferenced to any struct.
|
||||
*/
|
||||
int pin_config_get(const char *dev_name, const char *name,
|
||||
unsigned long *config)
|
||||
{
|
||||
struct pinctrl_dev *pctldev;
|
||||
int pin;
|
||||
|
||||
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
||||
if (!pctldev) {
|
||||
pin = -EINVAL;
|
||||
return pin;
|
||||
}
|
||||
|
||||
mutex_lock(&pctldev->mutex);
|
||||
|
||||
pin = pin_get_from_name(pctldev, name);
|
||||
if (pin < 0)
|
||||
goto unlock;
|
||||
|
||||
pin = pin_config_get_for_pin(pctldev, pin, config);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
return pin;
|
||||
}
|
||||
EXPORT_SYMBOL(pin_config_get);
|
||||
|
||||
static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
|
||||
unsigned long config)
|
||||
{
|
||||
const struct pinconf_ops *ops = pctldev->desc->confops;
|
||||
int ret;
|
||||
|
||||
if (!ops || !ops->pin_config_set) {
|
||||
dev_err(pctldev->dev, "cannot configure pin, missing "
|
||||
"config function in driver\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ops->pin_config_set(pctldev, pin, config);
|
||||
if (ret) {
|
||||
dev_err(pctldev->dev,
|
||||
"unable to set pin configuration on pin %d\n", pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pin_config_set() - set the configuration of a single pin parameter
|
||||
* @dev_name: name of pin controller device for this pin
|
||||
* @name: name of the pin to set the config for
|
||||
* @config: the config in this argument will contain the desired pin state, it
|
||||
* can be used directly by drivers as a numeral, or it can be dereferenced
|
||||
* to any struct.
|
||||
*/
|
||||
int pin_config_set(const char *dev_name, const char *name,
|
||||
unsigned long config)
|
||||
{
|
||||
struct pinctrl_dev *pctldev;
|
||||
int pin, ret;
|
||||
|
||||
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
||||
if (!pctldev) {
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&pctldev->mutex);
|
||||
|
||||
pin = pin_get_from_name(pctldev, name);
|
||||
if (pin < 0) {
|
||||
ret = pin;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = pin_config_set_for_pin(pctldev, pin, config);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pin_config_set);
|
||||
|
||||
int pin_config_group_get(const char *dev_name, const char *pin_group,
|
||||
unsigned long *config)
|
||||
{
|
||||
@@ -204,88 +112,6 @@ unlock:
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pin_config_group_get);
|
||||
|
||||
int pin_config_group_set(const char *dev_name, const char *pin_group,
|
||||
unsigned long config)
|
||||
{
|
||||
struct pinctrl_dev *pctldev;
|
||||
const struct pinconf_ops *ops;
|
||||
const struct pinctrl_ops *pctlops;
|
||||
int selector;
|
||||
const unsigned *pins;
|
||||
unsigned num_pins;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
||||
if (!pctldev) {
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&pctldev->mutex);
|
||||
|
||||
ops = pctldev->desc->confops;
|
||||
pctlops = pctldev->desc->pctlops;
|
||||
|
||||
if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) {
|
||||
dev_err(pctldev->dev, "cannot configure pin group, missing "
|
||||
"config function in driver\n");
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
selector = pinctrl_get_group_selector(pctldev, pin_group);
|
||||
if (selector < 0) {
|
||||
ret = selector;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins);
|
||||
if (ret) {
|
||||
dev_err(pctldev->dev, "cannot configure pin group, error "
|
||||
"getting pins\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the pin controller supports handling entire groups we use that
|
||||
* capability.
|
||||
*/
|
||||
if (ops->pin_config_group_set) {
|
||||
ret = ops->pin_config_group_set(pctldev, selector, config);
|
||||
/*
|
||||
* If the pin controller prefer that a certain group be handled
|
||||
* pin-by-pin as well, it returns -EAGAIN.
|
||||
*/
|
||||
if (ret != -EAGAIN)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the controller cannot handle entire groups, we configure each pin
|
||||
* individually.
|
||||
*/
|
||||
if (!ops->pin_config_set) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_pins; i++) {
|
||||
ret = ops->pin_config_set(pctldev, pins[i], config);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pin_config_group_set);
|
||||
|
||||
int pinconf_map_to_setting(struct pinctrl_map const *map,
|
||||
struct pinctrl_setting *setting)
|
||||
@@ -332,7 +158,7 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting)
|
||||
{
|
||||
struct pinctrl_dev *pctldev = setting->pctldev;
|
||||
const struct pinconf_ops *ops = pctldev->desc->confops;
|
||||
int i, ret;
|
||||
int ret, i;
|
||||
|
||||
if (!ops) {
|
||||
dev_err(pctldev->dev, "missing confops\n");
|
||||
@@ -341,39 +167,66 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting)
|
||||
|
||||
switch (setting->type) {
|
||||
case PIN_MAP_TYPE_CONFIGS_PIN:
|
||||
if (!ops->pin_config_set) {
|
||||
if (!ops->pin_config_set && !ops->pin_config_set_bulk) {
|
||||
dev_err(pctldev->dev, "missing pin_config_set op\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < setting->data.configs.num_configs; i++) {
|
||||
ret = ops->pin_config_set(pctldev,
|
||||
if (ops->pin_config_group_set_bulk) {
|
||||
ret = ops->pin_config_group_set_bulk(pctldev,
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
setting->data.configs.configs,
|
||||
setting->data.configs.num_configs);
|
||||
if (ret < 0) {
|
||||
dev_err(pctldev->dev,
|
||||
"pin_config_set op failed for pin %d config %08lx\n",
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
"pin_config_set op failed for pin %d\n",
|
||||
setting->data.configs.group_or_pin);
|
||||
return ret;
|
||||
}
|
||||
} else if (ops->pin_config_set) {
|
||||
for (i = 0; i < setting->data.configs.num_configs; i++) {
|
||||
ret = ops->pin_config_set(pctldev,
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(pctldev->dev,
|
||||
"pin_config_set op failed for pin %d config %08lx\n",
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PIN_MAP_TYPE_CONFIGS_GROUP:
|
||||
if (!ops->pin_config_group_set) {
|
||||
if (!ops->pin_config_group_set &&
|
||||
!ops->pin_config_group_set_bulk) {
|
||||
dev_err(pctldev->dev,
|
||||
"missing pin_config_group_set op\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < setting->data.configs.num_configs; i++) {
|
||||
ret = ops->pin_config_group_set(pctldev,
|
||||
if (ops->pin_config_group_set_bulk) {
|
||||
ret = ops->pin_config_group_set_bulk(pctldev,
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
setting->data.configs.configs,
|
||||
setting->data.configs.num_configs);
|
||||
if (ret < 0) {
|
||||
dev_err(pctldev->dev,
|
||||
"pin_config_group_set op failed for group %d config %08lx\n",
|
||||
"pin_config_group_set op failed for group %d\n",
|
||||
setting->data.configs.group_or_pin);
|
||||
return ret;
|
||||
}
|
||||
} else if (ops->pin_config_group_set) {
|
||||
for (i = 0; i < setting->data.configs.num_configs; i++) {
|
||||
ret = ops->pin_config_group_set(pctldev,
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
return ret;
|
||||
if (ret < 0) {
|
||||
dev_err(pctldev->dev,
|
||||
"pin_config_group_set op failed for group %d config %08lx\n",
|
||||
setting->data.configs.group_or_pin,
|
||||
setting->data.configs.configs[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -123,3 +123,9 @@ static inline void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_OF)
|
||||
int pinconf_generic_parse_dt_config(struct device_node *np,
|
||||
unsigned long **configs,
|
||||
unsigned int *nconfigs);
|
||||
#endif
|
||||
|
||||
141
drivers/pinctrl/pinctrl-utils.c
Normal file
141
drivers/pinctrl/pinctrl-utils.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Utils functions to implement the pincontrol driver.
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA Corporation.
|
||||
*
|
||||
* Author: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
|
||||
* whether express or implied; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include "core.h"
|
||||
#include "pinctrl-utils.h"
|
||||
|
||||
int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, unsigned reserve)
|
||||
{
|
||||
unsigned old_num = *reserved_maps;
|
||||
unsigned new_num = *num_maps + reserve;
|
||||
struct pinctrl_map *new_map;
|
||||
|
||||
if (old_num >= new_num)
|
||||
return 0;
|
||||
|
||||
new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
|
||||
if (!new_map) {
|
||||
dev_err(pctldev->dev, "krealloc(map) failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
|
||||
|
||||
*map = new_map;
|
||||
*reserved_maps = new_num;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_utils_reserve_map);
|
||||
|
||||
int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
const char *function)
|
||||
{
|
||||
if (WARN_ON(*num_maps == *reserved_maps))
|
||||
return -ENOSPC;
|
||||
|
||||
(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
|
||||
(*map)[*num_maps].data.mux.group = group;
|
||||
(*map)[*num_maps].data.mux.function = function;
|
||||
(*num_maps)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_mux);
|
||||
|
||||
int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
unsigned long *configs, unsigned num_configs,
|
||||
enum pinctrl_map_type type)
|
||||
{
|
||||
unsigned long *dup_configs;
|
||||
|
||||
if (WARN_ON(*num_maps == *reserved_maps))
|
||||
return -ENOSPC;
|
||||
|
||||
dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
|
||||
GFP_KERNEL);
|
||||
if (!dup_configs) {
|
||||
dev_err(pctldev->dev, "kmemdup(configs) failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
(*map)[*num_maps].type = type;
|
||||
(*map)[*num_maps].data.configs.group_or_pin = group;
|
||||
(*map)[*num_maps].data.configs.configs = dup_configs;
|
||||
(*map)[*num_maps].data.configs.num_configs = num_configs;
|
||||
(*num_maps)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_configs);
|
||||
|
||||
int pinctrl_utils_add_config(struct pinctrl_dev *pctldev,
|
||||
unsigned long **configs, unsigned *num_configs,
|
||||
unsigned long config)
|
||||
{
|
||||
unsigned old_num = *num_configs;
|
||||
unsigned new_num = old_num + 1;
|
||||
unsigned long *new_configs;
|
||||
|
||||
new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
|
||||
GFP_KERNEL);
|
||||
if (!new_configs) {
|
||||
dev_err(pctldev->dev, "krealloc(configs) failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
new_configs[old_num] = config;
|
||||
|
||||
*configs = new_configs;
|
||||
*num_configs = new_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_utils_add_config);
|
||||
|
||||
void pinctrl_utils_dt_free_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map *map, unsigned num_maps)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_maps; i++) {
|
||||
switch (map[i].type) {
|
||||
case PIN_MAP_TYPE_CONFIGS_GROUP:
|
||||
case PIN_MAP_TYPE_CONFIGS_PIN:
|
||||
kfree(map[i].data.configs.configs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_utils_dt_free_map);
|
||||
43
drivers/pinctrl/pinctrl-utils.h
Normal file
43
drivers/pinctrl/pinctrl-utils.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Utils functions to implement the pincontrol driver.
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA Corporation.
|
||||
*
|
||||
* Author: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
|
||||
* whether express or implied; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
#ifndef __PINCTRL_UTILS_H__
|
||||
#define __PINCTRL_UTILS_H__
|
||||
|
||||
int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, unsigned reserve);
|
||||
int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
const char *function);
|
||||
int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps, const char *group,
|
||||
unsigned long *configs, unsigned num_configs,
|
||||
enum pinctrl_map_type type);
|
||||
int pinctrl_utils_add_config(struct pinctrl_dev *pctldev,
|
||||
unsigned long **configs, unsigned *num_configs,
|
||||
unsigned long config);
|
||||
void pinctrl_utils_dt_free_map(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_map *map, unsigned num_maps);
|
||||
|
||||
#endif /* __PINCTRL_UTILS_H__ */
|
||||
@@ -158,47 +158,4 @@ static inline struct pinctrl * __must_check devm_pinctrl_get_select_default(
|
||||
return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PINCONF
|
||||
|
||||
extern int pin_config_get(const char *dev_name, const char *name,
|
||||
unsigned long *config);
|
||||
extern int pin_config_set(const char *dev_name, const char *name,
|
||||
unsigned long config);
|
||||
extern int pin_config_group_get(const char *dev_name,
|
||||
const char *pin_group,
|
||||
unsigned long *config);
|
||||
extern int pin_config_group_set(const char *dev_name,
|
||||
const char *pin_group,
|
||||
unsigned long config);
|
||||
|
||||
#else
|
||||
|
||||
static inline int pin_config_get(const char *dev_name, const char *name,
|
||||
unsigned long *config)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pin_config_set(const char *dev_name, const char *name,
|
||||
unsigned long config)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pin_config_group_get(const char *dev_name,
|
||||
const char *pin_group,
|
||||
unsigned long *config)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pin_config_group_set(const char *dev_name,
|
||||
const char *pin_group,
|
||||
unsigned long config)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_PINCTRL_CONSUMER_H */
|
||||
|
||||
@@ -29,12 +29,21 @@
|
||||
* if for example some other pin is going to drive the signal connected
|
||||
* to it for a while. Pins used for input are usually always high
|
||||
* impedance.
|
||||
* @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
|
||||
* weakly drives the last value on a tristate bus, also known as a "bus
|
||||
* holder", "bus keeper" or "repeater". This allows another device on the
|
||||
* bus to change the value by driving the bus high or low and switching to
|
||||
* tristate. The argument is ignored.
|
||||
* @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
|
||||
* impedance to VDD). If the argument is != 0 pull-up is enabled,
|
||||
* if it is 0, pull-up is disabled.
|
||||
* @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
|
||||
* impedance to GROUND). If the argument is != 0 pull-down is enabled,
|
||||
* if it is 0, pull-down is disabled.
|
||||
* @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based
|
||||
* on embedded knowledge of the controller, like current mux function.
|
||||
* If the argument is != 0 pull up/down is enabled, if it is 0,
|
||||
* the pull is disabled.
|
||||
* @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
|
||||
* low, this is the most typical case and is typically achieved with two
|
||||
* active transistors on the output. Setting this config will enable
|
||||
@@ -48,6 +57,9 @@
|
||||
* argument is ignored.
|
||||
* @PIN_CONFIG_DRIVE_STRENGTH: the pin will sink or source at most the current
|
||||
* passed as argument. The argument is in mA.
|
||||
* @PIN_CONFIG_INPUT_ENABLE: enable the pin's input. Note that this does not
|
||||
* affect the pin's ability to drive output. 1 enables input, 0 disables
|
||||
* input.
|
||||
* @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
|
||||
* If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
|
||||
* schmitt-trigger mode is disabled.
|
||||
@@ -57,7 +69,7 @@
|
||||
* setting pins to this mode.
|
||||
* @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
|
||||
* which means it will wait for signals to settle when reading inputs. The
|
||||
* argument gives the debounce time on a custom format. Setting the
|
||||
* argument gives the debounce time in usecs. Setting the
|
||||
* argument to zero turns debouncing off.
|
||||
* @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
|
||||
* supplies, the argument to this parameter (on a custom format) tells
|
||||
@@ -78,12 +90,15 @@
|
||||
enum pin_config_param {
|
||||
PIN_CONFIG_BIAS_DISABLE,
|
||||
PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
|
||||
PIN_CONFIG_BIAS_BUS_HOLD,
|
||||
PIN_CONFIG_BIAS_PULL_UP,
|
||||
PIN_CONFIG_BIAS_PULL_DOWN,
|
||||
PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
|
||||
PIN_CONFIG_DRIVE_PUSH_PULL,
|
||||
PIN_CONFIG_DRIVE_OPEN_DRAIN,
|
||||
PIN_CONFIG_DRIVE_OPEN_SOURCE,
|
||||
PIN_CONFIG_DRIVE_STRENGTH,
|
||||
PIN_CONFIG_INPUT_ENABLE,
|
||||
PIN_CONFIG_INPUT_SCHMITT_ENABLE,
|
||||
PIN_CONFIG_INPUT_SCHMITT,
|
||||
PIN_CONFIG_INPUT_DEBOUNCE,
|
||||
@@ -122,6 +137,58 @@ static inline unsigned long pinconf_to_config_packed(enum pin_config_param param
|
||||
return PIN_CONF_PACKED(param, argument);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
struct pinctrl_dev;
|
||||
struct pinctrl_map;
|
||||
|
||||
int pinconf_generic_dt_subnode_to_map_new(struct pinctrl_dev *pctldev,
|
||||
struct device_node *np, struct pinctrl_map **map,
|
||||
unsigned *reserved_maps, unsigned *num_maps,
|
||||
enum pinctrl_map_type type);
|
||||
int pinconf_generic_dt_node_to_map_new(struct pinctrl_dev *pctldev,
|
||||
struct device_node *np_config, struct pinctrl_map **map,
|
||||
unsigned *num_maps, enum pinctrl_map_type type);
|
||||
|
||||
static inline int pinconf_generic_dt_node_to_map_group(
|
||||
struct pinctrl_dev *pctldev, struct device_node *np_config,
|
||||
struct pinctrl_map **map, unsigned *num_maps)
|
||||
{
|
||||
return pinconf_generic_dt_node_to_map_new(pctldev, np_config, map, num_maps,
|
||||
PIN_MAP_TYPE_CONFIGS_GROUP);
|
||||
}
|
||||
|
||||
static inline 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)
|
||||
{
|
||||
return pinconf_generic_dt_subnode_to_map_new(pctldev, np, map,
|
||||
reserved_maps, num_maps,
|
||||
PIN_MAP_TYPE_CONFIGS_PIN);
|
||||
}
|
||||
|
||||
static inline int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev,
|
||||
struct device_node *np_config, struct pinctrl_map **map,
|
||||
unsigned *num_maps)
|
||||
{
|
||||
return pinconf_generic_dt_node_to_map_new(pctldev, np_config,
|
||||
map, num_maps,
|
||||
PIN_MAP_TYPE_CONFIGS_PIN);
|
||||
}
|
||||
|
||||
|
||||
static inline int pinconf_generic_dt_node_to_map_pin(
|
||||
struct pinctrl_dev *pctldev, struct device_node *np_config,
|
||||
struct pinctrl_map **map, unsigned *num_maps)
|
||||
{
|
||||
return pinconf_generic_dt_node_to_map_new(pctldev, np_config, map, num_maps,
|
||||
PIN_MAP_TYPE_CONFIGS_PIN);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_GENERIC_PINCONF */
|
||||
|
||||
#endif /* __LINUX_PINCTRL_PINCONF_GENERIC_H */
|
||||
|
||||
@@ -48,12 +48,20 @@ struct pinconf_ops {
|
||||
int (*pin_config_set) (struct pinctrl_dev *pctldev,
|
||||
unsigned pin,
|
||||
unsigned long config);
|
||||
int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev,
|
||||
unsigned pin,
|
||||
unsigned long *configs,
|
||||
unsigned num_configs);
|
||||
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
|
||||
unsigned selector,
|
||||
unsigned long *config);
|
||||
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
|
||||
unsigned selector,
|
||||
unsigned long config);
|
||||
int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev,
|
||||
unsigned selector,
|
||||
unsigned long *configs,
|
||||
unsigned num_configs);
|
||||
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
|
||||
const char *arg,
|
||||
unsigned long *config);
|
||||
|
||||
@@ -32,10 +32,12 @@ struct device_node;
|
||||
* pins, pads or other muxable units in this struct
|
||||
* @number: unique pin number from the global pin number space
|
||||
* @name: a name for this pin
|
||||
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
|
||||
*/
|
||||
struct pinctrl_pin_desc {
|
||||
unsigned number;
|
||||
const char *name;
|
||||
void *drv_data;
|
||||
};
|
||||
|
||||
/* Convenience macro to define a single named or anonymous pin descriptor */
|
||||
|
||||
Reference in New Issue
Block a user