ad6e1107ba
There are a few places in the api where the code simply returns -EINVAL when it finds an error. An example is pinmux_map_to_setting() which now reports an error if we try to match a group with a function that it does not support. The reporting of errors in pinconf_check_ops and pinmux_check_ops now has the same style and is located inside the according functions and not the calling code. When the map is found in the DT but the default state can not be selected we get an error to know that the code at least tried. The patch also removes a stray word from one comment and a "->" from another for the sake of consistency. Finally we replace a few pr_err/debug() calls with dev_err/dbg(). Thanks go to Stephen Warren for reviewing the patch and enhancing the reporting inside pinmux_map_to_setting(). Signed-off-by: John Crispin <blogic@openwrt.org> Acked-by: Stephen Warren <swarren@wwwdotorg.org> Cc: linux-kernel@vger.kernel.org Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
591 lines
14 KiB
C
591 lines
14 KiB
C
/*
|
|
* Core driver for the pin config portions of the pin control subsystem
|
|
*
|
|
* Copyright (C) 2011 ST-Ericsson SA
|
|
* 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) "pinconfig core: " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/pinctrl/machine.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include "core.h"
|
|
#include "pinconf.h"
|
|
|
|
int pinconf_check_ops(struct pinctrl_dev *pctldev)
|
|
{
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
|
|
/* We must be able to read out pin status */
|
|
if (!ops->pin_config_get && !ops->pin_config_group_get) {
|
|
dev_err(pctldev->dev,
|
|
"pinconf must be able to read out pin status\n");
|
|
return -EINVAL;
|
|
}
|
|
/* We have to be able to config the pins in SOME way */
|
|
if (!ops->pin_config_set && !ops->pin_config_group_set) {
|
|
dev_err(pctldev->dev,
|
|
"pinconf has to be able to set a pins config\n");
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int pinconf_validate_map(struct pinctrl_map const *map, int i)
|
|
{
|
|
if (!map->data.configs.group_or_pin) {
|
|
pr_err("failed to register map %s (%d): no group/pin given\n",
|
|
map->name, i);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (map->data.configs.num_configs &&
|
|
!map->data.configs.configs) {
|
|
pr_err("failed to register map %s (%d): no configs ptr given\n",
|
|
map->name, i);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
|
|
unsigned long *config)
|
|
{
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
|
|
if (!ops || !ops->pin_config_get) {
|
|
dev_err(pctldev->dev, "cannot get pin configuration, missing "
|
|
"pin_config_get() function in driver\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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;
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
|
if (!pctldev) {
|
|
pin = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
pin = pin_get_from_name(pctldev, name);
|
|
if (pin < 0)
|
|
goto unlock;
|
|
|
|
pin = pin_config_get_for_pin(pctldev, pin, config);
|
|
|
|
unlock:
|
|
mutex_unlock(&pinctrl_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;
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
|
if (!pctldev) {
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
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(&pinctrl_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(pin_config_set);
|
|
|
|
int pin_config_group_get(const char *dev_name, const char *pin_group,
|
|
unsigned long *config)
|
|
{
|
|
struct pinctrl_dev *pctldev;
|
|
const struct pinconf_ops *ops;
|
|
int selector, ret;
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
|
if (!pctldev) {
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
ops = pctldev->desc->confops;
|
|
|
|
if (!ops || !ops->pin_config_group_get) {
|
|
dev_err(pctldev->dev, "cannot get configuration for pin "
|
|
"group, missing group config get function in "
|
|
"driver\n");
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
selector = pinctrl_get_group_selector(pctldev, pin_group);
|
|
if (selector < 0) {
|
|
ret = selector;
|
|
goto unlock;
|
|
}
|
|
|
|
ret = ops->pin_config_group_get(pctldev, selector, config);
|
|
|
|
unlock:
|
|
mutex_unlock(&pinctrl_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;
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
pctldev = get_pinctrl_dev_from_devname(dev_name);
|
|
if (!pctldev) {
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
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(&pinctrl_mutex);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(pin_config_group_set);
|
|
|
|
int pinconf_map_to_setting(struct pinctrl_map const *map,
|
|
struct pinctrl_setting *setting)
|
|
{
|
|
struct pinctrl_dev *pctldev = setting->pctldev;
|
|
int pin;
|
|
|
|
switch (setting->type) {
|
|
case PIN_MAP_TYPE_CONFIGS_PIN:
|
|
pin = pin_get_from_name(pctldev,
|
|
map->data.configs.group_or_pin);
|
|
if (pin < 0) {
|
|
dev_err(pctldev->dev, "could not map pin config for \"%s\"",
|
|
map->data.configs.group_or_pin);
|
|
return pin;
|
|
}
|
|
setting->data.configs.group_or_pin = pin;
|
|
break;
|
|
case PIN_MAP_TYPE_CONFIGS_GROUP:
|
|
pin = pinctrl_get_group_selector(pctldev,
|
|
map->data.configs.group_or_pin);
|
|
if (pin < 0) {
|
|
dev_err(pctldev->dev, "could not map group config for \"%s\"",
|
|
map->data.configs.group_or_pin);
|
|
return pin;
|
|
}
|
|
setting->data.configs.group_or_pin = pin;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
setting->data.configs.num_configs = map->data.configs.num_configs;
|
|
setting->data.configs.configs = map->data.configs.configs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void pinconf_free_setting(struct pinctrl_setting const *setting)
|
|
{
|
|
}
|
|
|
|
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;
|
|
|
|
if (!ops) {
|
|
dev_err(pctldev->dev, "missing confops\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (setting->type) {
|
|
case PIN_MAP_TYPE_CONFIGS_PIN:
|
|
if (!ops->pin_config_set) {
|
|
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,
|
|
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) {
|
|
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,
|
|
setting->data.configs.group_or_pin,
|
|
setting->data.configs.configs[i]);
|
|
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;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map)
|
|
{
|
|
struct pinctrl_dev *pctldev;
|
|
const struct pinconf_ops *confops;
|
|
int i;
|
|
|
|
pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
|
|
if (pctldev)
|
|
confops = pctldev->desc->confops;
|
|
else
|
|
confops = NULL;
|
|
|
|
switch (map->type) {
|
|
case PIN_MAP_TYPE_CONFIGS_PIN:
|
|
seq_printf(s, "pin ");
|
|
break;
|
|
case PIN_MAP_TYPE_CONFIGS_GROUP:
|
|
seq_printf(s, "group ");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
seq_printf(s, "%s\n", map->data.configs.group_or_pin);
|
|
|
|
for (i = 0; i < map->data.configs.num_configs; i++) {
|
|
seq_printf(s, "config ");
|
|
if (confops && confops->pin_config_config_dbg_show)
|
|
confops->pin_config_config_dbg_show(pctldev, s,
|
|
map->data.configs.configs[i]);
|
|
else
|
|
seq_printf(s, "%08lx", map->data.configs.configs[i]);
|
|
seq_printf(s, "\n");
|
|
}
|
|
}
|
|
|
|
void pinconf_show_setting(struct seq_file *s,
|
|
struct pinctrl_setting const *setting)
|
|
{
|
|
struct pinctrl_dev *pctldev = setting->pctldev;
|
|
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
|
|
const struct pinconf_ops *confops = pctldev->desc->confops;
|
|
struct pin_desc *desc;
|
|
int i;
|
|
|
|
switch (setting->type) {
|
|
case PIN_MAP_TYPE_CONFIGS_PIN:
|
|
desc = pin_desc_get(setting->pctldev,
|
|
setting->data.configs.group_or_pin);
|
|
seq_printf(s, "pin %s (%d)",
|
|
desc->name ? desc->name : "unnamed",
|
|
setting->data.configs.group_or_pin);
|
|
break;
|
|
case PIN_MAP_TYPE_CONFIGS_GROUP:
|
|
seq_printf(s, "group %s (%d)",
|
|
pctlops->get_group_name(pctldev,
|
|
setting->data.configs.group_or_pin),
|
|
setting->data.configs.group_or_pin);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* FIXME: We should really get the pin controler to dump the config
|
|
* values, so they can be decoded to something meaningful.
|
|
*/
|
|
for (i = 0; i < setting->data.configs.num_configs; i++) {
|
|
seq_printf(s, " ");
|
|
if (confops && confops->pin_config_config_dbg_show)
|
|
confops->pin_config_config_dbg_show(pctldev, s,
|
|
setting->data.configs.configs[i]);
|
|
else
|
|
seq_printf(s, "%08lx",
|
|
setting->data.configs.configs[i]);
|
|
}
|
|
|
|
seq_printf(s, "\n");
|
|
}
|
|
|
|
static void pinconf_dump_pin(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, int pin)
|
|
{
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
|
|
/* no-op when not using generic pin config */
|
|
pinconf_generic_dump_pin(pctldev, s, pin);
|
|
if (ops && ops->pin_config_dbg_show)
|
|
ops->pin_config_dbg_show(pctldev, s, pin);
|
|
}
|
|
|
|
static int pinconf_pins_show(struct seq_file *s, void *what)
|
|
{
|
|
struct pinctrl_dev *pctldev = s->private;
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
unsigned i, pin;
|
|
|
|
if (!ops || !ops->pin_config_get)
|
|
return 0;
|
|
|
|
seq_puts(s, "Pin config settings per pin\n");
|
|
seq_puts(s, "Format: pin (name): configs\n");
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
/* The pin number can be retrived from the pin controller descriptor */
|
|
for (i = 0; i < pctldev->desc->npins; i++) {
|
|
struct pin_desc *desc;
|
|
|
|
pin = pctldev->desc->pins[i].number;
|
|
desc = pin_desc_get(pctldev, pin);
|
|
/* Skip if we cannot search the pin */
|
|
if (desc == NULL)
|
|
continue;
|
|
|
|
seq_printf(s, "pin %d (%s):", pin,
|
|
desc->name ? desc->name : "unnamed");
|
|
|
|
pinconf_dump_pin(pctldev, s, pin);
|
|
|
|
seq_printf(s, "\n");
|
|
}
|
|
|
|
mutex_unlock(&pinctrl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pinconf_dump_group(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, unsigned selector,
|
|
const char *gname)
|
|
{
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
|
|
/* no-op when not using generic pin config */
|
|
pinconf_generic_dump_group(pctldev, s, gname);
|
|
if (ops && ops->pin_config_group_dbg_show)
|
|
ops->pin_config_group_dbg_show(pctldev, s, selector);
|
|
}
|
|
|
|
static int pinconf_groups_show(struct seq_file *s, void *what)
|
|
{
|
|
struct pinctrl_dev *pctldev = s->private;
|
|
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
|
|
const struct pinconf_ops *ops = pctldev->desc->confops;
|
|
unsigned ngroups = pctlops->get_groups_count(pctldev);
|
|
unsigned selector = 0;
|
|
|
|
if (!ops || !ops->pin_config_group_get)
|
|
return 0;
|
|
|
|
seq_puts(s, "Pin config settings per pin group\n");
|
|
seq_puts(s, "Format: group (name): configs\n");
|
|
|
|
mutex_lock(&pinctrl_mutex);
|
|
|
|
while (selector < ngroups) {
|
|
const char *gname = pctlops->get_group_name(pctldev, selector);
|
|
|
|
seq_printf(s, "%u (%s):", selector, gname);
|
|
pinconf_dump_group(pctldev, s, selector, gname);
|
|
seq_printf(s, "\n");
|
|
|
|
selector++;
|
|
}
|
|
|
|
mutex_unlock(&pinctrl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pinconf_pins_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, pinconf_pins_show, inode->i_private);
|
|
}
|
|
|
|
static int pinconf_groups_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, pinconf_groups_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations pinconf_pins_ops = {
|
|
.open = pinconf_pins_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static const struct file_operations pinconf_groups_ops = {
|
|
.open = pinconf_groups_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
void pinconf_init_device_debugfs(struct dentry *devroot,
|
|
struct pinctrl_dev *pctldev)
|
|
{
|
|
debugfs_create_file("pinconf-pins", S_IFREG | S_IRUGO,
|
|
devroot, pctldev, &pinconf_pins_ops);
|
|
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
|
|
devroot, pctldev, &pinconf_groups_ops);
|
|
}
|
|
|
|
#endif
|