summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/pinmux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinmux.c')
-rw-r--r--drivers/pinctrl/pinmux.c693
1 files changed, 531 insertions, 162 deletions
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 88cc5095d0c9..3a8dd184ba3d 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Core driver for the pin muxing portions of the pin control subsystem
*
@@ -8,40 +9,42 @@
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
- *
- * License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) "pinmux core: " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
+#include <linux/array_size.h>
+#include <linux/ctype.h>
+#include <linux/cleanup.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/radix-tree.h>
#include <linux/err.h>
+#include <linux/init.h>
#include <linux/list.h>
-#include <linux/string.h>
-#include <linux/sysfs.h>
-#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/radix-tree.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
+
#include "core.h"
#include "pinmux.h"
int pinmux_check_ops(struct pinctrl_dev *pctldev)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
- unsigned nfuncs;
- unsigned selector = 0;
+ unsigned int nfuncs;
+ unsigned int selector = 0;
/* Check that we implement required operations */
if (!ops ||
!ops->get_functions_count ||
!ops->get_function_name ||
!ops->get_function_groups ||
- !ops->enable) {
+ !ops->set_mux) {
dev_err(pctldev->dev, "pinmux ops lacks necessary functions\n");
return -EINVAL;
}
@@ -61,7 +64,7 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev)
return 0;
}
-int pinmux_validate_map(struct pinctrl_map const *map, int i)
+int pinmux_validate_map(const struct pinctrl_map *map, int i)
{
if (!map->data.mux.function) {
pr_err("failed to register map %s (%d): no function given\n",
@@ -73,7 +76,41 @@ int pinmux_validate_map(struct pinctrl_map const *map, int i)
}
/**
+ * pinmux_can_be_used_for_gpio() - check if a specific pin
+ * is either muxed to a different function or used as gpio.
+ *
+ * @pctldev: the associated pin controller device
+ * @pin: the pin number in the global pin space
+ *
+ * Controllers not defined as strict will always return true,
+ * menaning that the gpio can be used.
+ */
+bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned int pin)
+{
+ struct pin_desc *desc = pin_desc_get(pctldev, pin);
+ const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ const struct pinctrl_setting_mux *mux_setting;
+ bool func_is_gpio = false;
+
+ /* Can't inspect pin, assume it can be used */
+ if (!desc || !ops)
+ return true;
+
+ mux_setting = desc->mux_setting;
+
+ guard(mutex)(&desc->mux_lock);
+ if (mux_setting && ops->function_is_gpio)
+ func_is_gpio = ops->function_is_gpio(pctldev, mux_setting->func);
+
+ if (ops->strict && desc->mux_usecount && !func_is_gpio)
+ return false;
+
+ return !(ops->strict && !!desc->gpio_owner);
+}
+
+/**
* pin_request() - request a single pin to be muxed in, typically for GPIO
+ * @pctldev: the associated pin controller device
* @pin: the pin number in the global pin space
* @owner: a representation of the owner of this pin; typically the device
* name that controls its mux function, or the requested GPIO name
@@ -86,7 +123,9 @@ static int pin_request(struct pinctrl_dev *pctldev,
{
struct pin_desc *desc;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ const struct pinctrl_setting_mux *mux_setting;
int status = -EINVAL;
+ bool gpio_ok = false;
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
@@ -96,32 +135,44 @@ static int pin_request(struct pinctrl_dev *pctldev,
goto out;
}
+ mux_setting = desc->mux_setting;
+
dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
pin, desc->name, owner);
- if (gpio_range) {
- /* There's no need to support multiple GPIO requests */
- if (desc->gpio_owner) {
+ scoped_guard(mutex, &desc->mux_lock) {
+ if (mux_setting) {
+ if (ops->function_is_gpio)
+ gpio_ok = ops->function_is_gpio(pctldev,
+ mux_setting->func);
+ } else {
+ gpio_ok = true;
+ }
+
+ if ((!gpio_range || ops->strict) && !gpio_ok &&
+ desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
dev_err(pctldev->dev,
"pin %s already requested by %s; cannot claim for %s\n",
- desc->name, desc->gpio_owner, owner);
+ desc->name, desc->mux_owner, owner);
goto out;
}
- desc->gpio_owner = owner;
- } else {
- if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
+ if ((gpio_range || ops->strict) && !gpio_ok && desc->gpio_owner) {
dev_err(pctldev->dev,
"pin %s already requested by %s; cannot claim for %s\n",
- desc->name, desc->mux_owner, owner);
+ desc->name, desc->gpio_owner, owner);
goto out;
}
- desc->mux_usecount++;
- if (desc->mux_usecount > 1)
- return 0;
+ if (gpio_range) {
+ desc->gpio_owner = owner;
+ } else {
+ desc->mux_usecount++;
+ if (desc->mux_usecount > 1)
+ return 0;
- desc->mux_owner = owner;
+ desc->mux_owner = owner;
+ }
}
/* Let each pin increase references to this module */
@@ -145,25 +196,25 @@ static int pin_request(struct pinctrl_dev *pctldev,
else
status = 0;
- if (status) {
- dev_err(pctldev->dev, "request() failed for pin %d\n", pin);
+ if (status)
module_put(pctldev->owner);
- }
out_free_pin:
if (status) {
- if (gpio_range) {
- desc->gpio_owner = NULL;
- } else {
- desc->mux_usecount--;
- if (!desc->mux_usecount)
- desc->mux_owner = NULL;
+ scoped_guard(mutex, &desc->mux_lock) {
+ if (gpio_range) {
+ desc->gpio_owner = NULL;
+ } else {
+ desc->mux_usecount--;
+ if (!desc->mux_usecount)
+ desc->mux_owner = NULL;
+ }
}
}
out:
if (status)
- dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
- pin, owner, status);
+ dev_err_probe(pctldev->dev, status, "pin-%d (%s)\n",
+ pin, owner);
return status;
}
@@ -193,15 +244,26 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
return NULL;
}
- if (!gpio_range) {
- /*
- * A pin should not be freed more times than allocated.
- */
- if (WARN_ON(!desc->mux_usecount))
- return NULL;
- desc->mux_usecount--;
- if (desc->mux_usecount)
- return NULL;
+ scoped_guard(mutex, &desc->mux_lock) {
+ if (!gpio_range) {
+ /*
+ * A pin should not be freed more times than allocated.
+ */
+ if (WARN_ON(!desc->mux_usecount))
+ return NULL;
+ desc->mux_usecount--;
+ if (desc->mux_usecount)
+ return NULL;
+ }
+
+ if (gpio_range) {
+ owner = desc->gpio_owner;
+ desc->gpio_owner = NULL;
+ } else {
+ owner = desc->mux_owner;
+ desc->mux_owner = NULL;
+ desc->mux_setting = NULL;
+ }
}
/*
@@ -213,15 +275,6 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
else if (ops->free)
ops->free(pctldev, pin);
- if (gpio_range) {
- owner = desc->gpio_owner;
- desc->gpio_owner = NULL;
- } else {
- owner = desc->mux_owner;
- desc->mux_owner = NULL;
- desc->mux_setting = NULL;
- }
-
module_put(pctldev->owner);
return owner;
@@ -232,10 +285,11 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
* @pctldev: pin controller device affected
* @pin: the pin to mux in for GPIO
* @range: the applicable GPIO range
+ * @gpio: number of requested GPIO
*/
int pinmux_request_gpio(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
- unsigned pin, unsigned gpio)
+ unsigned int pin, unsigned int gpio)
{
const char *owner;
int ret;
@@ -243,7 +297,7 @@ int pinmux_request_gpio(struct pinctrl_dev *pctldev,
/* Conjure some name stating what chip and pin this is taken by */
owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
if (!owner)
- return -EINVAL;
+ return -ENOMEM;
ret = pin_request(pctldev, pin, owner, range);
if (ret < 0)
@@ -258,7 +312,7 @@ int pinmux_request_gpio(struct pinctrl_dev *pctldev,
* @pin: the affected currently GPIO-muxed in pin
* @range: applicable GPIO range
*/
-void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin,
+void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned int pin,
struct pinctrl_gpio_range *range)
{
const char *owner;
@@ -276,7 +330,7 @@ void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin,
*/
int pinmux_gpio_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
- unsigned pin, bool input)
+ unsigned int pin, bool input)
{
const struct pinmux_ops *ops;
int ret;
@@ -295,35 +349,31 @@ static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev,
const char *function)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
- unsigned nfuncs = ops->get_functions_count(pctldev);
- unsigned selector = 0;
+ unsigned int nfuncs = ops->get_functions_count(pctldev);
+ unsigned int selector = 0;
/* See if this pctldev has this function */
while (selector < nfuncs) {
- const char *fname = ops->get_function_name(pctldev,
- selector);
+ const char *fname = ops->get_function_name(pctldev, selector);
- if (!strcmp(function, fname))
+ if (fname && !strcmp(function, fname))
return selector;
selector++;
}
- pr_err("%s does not support function %s\n",
- pinctrl_dev_get_name(pctldev), function);
return -EINVAL;
}
-int pinmux_map_to_setting(struct pinctrl_map const *map,
+int pinmux_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
char const * const *groups;
- unsigned num_groups;
+ unsigned int num_groups;
int ret;
const char *group;
- int i;
if (!pmxops) {
dev_err(pctldev->dev, "does not support mux function\n");
@@ -352,19 +402,13 @@ int pinmux_map_to_setting(struct pinctrl_map const *map,
return -EINVAL;
}
if (map->data.mux.group) {
- bool found = false;
group = map->data.mux.group;
- for (i = 0; i < num_groups; i++) {
- if (!strcmp(group, groups[i])) {
- found = true;
- break;
- }
- }
- if (!found) {
+ ret = match_string(groups, num_groups, group);
+ if (ret < 0) {
dev_err(pctldev->dev,
"invalid group \"%s\" for function \"%s\"\n",
group, map->data.mux.function);
- return -EINVAL;
+ return ret;
}
} else {
group = groups[0];
@@ -381,29 +425,35 @@ int pinmux_map_to_setting(struct pinctrl_map const *map,
return 0;
}
-void pinmux_free_setting(struct pinctrl_setting const *setting)
+void pinmux_free_setting(const struct pinctrl_setting *setting)
{
/* This function is currently unused */
}
-int pinmux_enable_setting(struct pinctrl_setting const *setting)
+int pinmux_enable_setting(const struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
- int ret;
- const unsigned *pins;
- unsigned num_pins;
+ int ret = 0;
+ const unsigned int *pins = NULL;
+ unsigned int num_pins = 0;
int i;
struct pin_desc *desc;
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
- &pins, &num_pins);
+ if (pctlops->get_group_pins)
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
+ &pins, &num_pins);
+
if (ret) {
+ const char *gname;
+
/* errors only affect debug data, so just warn */
+ gname = pctlops->get_group_name(pctldev,
+ setting->data.mux.group);
dev_warn(pctldev->dev,
- "could not get pins for group selector %d\n",
- setting->data.mux.group);
+ "could not get pins for group %s\n",
+ gname);
num_pins = 0;
}
@@ -411,9 +461,17 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting)
for (i = 0; i < num_pins; i++) {
ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
if (ret) {
- dev_err(pctldev->dev,
- "could not request pin %d on device %s\n",
- pins[i], pinctrl_dev_get_name(pctldev));
+ const char *gname;
+ const char *pname;
+
+ desc = pin_desc_get(pctldev, pins[i]);
+ pname = desc ? desc->name : "non-existing";
+ gname = pctlops->get_group_name(pctldev,
+ setting->data.mux.group);
+ dev_err_probe(pctldev->dev, ret,
+ "could not request pin %d (%s) from group %s on device %s\n",
+ pins[i], pname, gname,
+ pinctrl_dev_get_name(pctldev));
goto err_pin_request;
}
}
@@ -427,22 +485,25 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting)
pins[i]);
continue;
}
- desc->mux_setting = &(setting->data.mux);
+ scoped_guard(mutex, &desc->mux_lock)
+ desc->mux_setting = &(setting->data.mux);
}
- ret = ops->enable(pctldev, setting->data.mux.func,
- setting->data.mux.group);
+ ret = ops->set_mux(pctldev, setting->data.mux.func,
+ setting->data.mux.group);
if (ret)
- goto err_enable;
+ goto err_set_mux;
return 0;
-err_enable:
+err_set_mux:
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
- if (desc)
- desc->mux_setting = NULL;
+ if (desc) {
+ scoped_guard(mutex, &desc->mux_lock)
+ desc->mux_setting = NULL;
+ }
}
err_pin_request:
/* On error release all taken pins */
@@ -452,24 +513,29 @@ err_pin_request:
return ret;
}
-void pinmux_disable_setting(struct pinctrl_setting const *setting)
+void pinmux_disable_setting(const struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- const struct pinmux_ops *ops = pctldev->desc->pmxops;
- int ret;
- const unsigned *pins;
- unsigned num_pins;
+ int ret = 0;
+ const unsigned int *pins = NULL;
+ unsigned int num_pins = 0;
int i;
struct pin_desc *desc;
+ bool is_equal;
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
- &pins, &num_pins);
+ if (pctlops->get_group_pins)
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
+ &pins, &num_pins);
if (ret) {
+ const char *gname;
+
/* errors only affect debug data, so just warn */
+ gname = pctlops->get_group_name(pctldev,
+ setting->data.mux.group);
dev_warn(pctldev->dev,
- "could not get pins for group selector %d\n",
- setting->data.mux.group);
+ "could not get pins for group %s\n",
+ gname);
num_pins = 0;
}
@@ -482,15 +548,21 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting)
pins[i]);
continue;
}
- desc->mux_setting = NULL;
- }
+ scoped_guard(mutex, &desc->mux_lock)
+ is_equal = (desc->mux_setting == &(setting->data.mux));
- /* And release the pins */
- for (i = 0; i < num_pins; i++)
- pin_free(pctldev, pins[i], NULL);
+ if (is_equal) {
+ pin_free(pctldev, pins[i], NULL);
+ } else {
+ const char *gname;
- if (ops->disable)
- ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
+ gname = pctlops->get_group_name(pctldev,
+ setting->data.mux.group);
+ dev_warn(pctldev->dev,
+ "not freeing pin %d (%s) as part of deactivating group %s - it is already used for some other setting",
+ pins[i], desc->name, gname);
+ }
+ }
}
#ifdef CONFIG_DEBUG_FS
@@ -500,8 +572,8 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- unsigned nfuncs;
- unsigned func_selector = 0;
+ unsigned int nfuncs;
+ unsigned int func_selector = 0;
if (!pmxops)
return 0;
@@ -512,17 +584,20 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
const char *func = pmxops->get_function_name(pctldev,
func_selector);
const char * const *groups;
- unsigned num_groups;
+ unsigned int num_groups;
int ret;
int i;
ret = pmxops->get_function_groups(pctldev, func_selector,
&groups, &num_groups);
- if (ret)
+ if (ret) {
seq_printf(s, "function %s: COULD NOT GET GROUPS\n",
func);
+ func_selector++;
+ continue;
+ }
- seq_printf(s, "function: %s, groups = [ ", func);
+ seq_printf(s, "function %d: %s, groups = [ ", func_selector, func);
for (i = 0; i < num_groups; i++)
seq_printf(s, "%s ", groups[i]);
seq_puts(s, "]\n");
@@ -534,19 +609,25 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinmux_functions);
static int pinmux_pins_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- unsigned i, pin;
+ unsigned int i, pin;
if (!pmxops)
return 0;
seq_puts(s, "Pinmux settings per pin\n");
- seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n");
+ if (pmxops->strict)
+ seq_puts(s,
+ "Format: pin (name): mux_owner|gpio_owner (strict) hog?\n");
+ else
+ seq_puts(s,
+ "Format: pin (name): mux_owner gpio_owner hog?\n");
mutex_lock(&pctldev->mutex);
@@ -561,34 +642,51 @@ static int pinmux_pins_show(struct seq_file *s, void *what)
if (desc == NULL)
continue;
- if (desc->mux_owner &&
- !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
- is_hog = true;
-
- seq_printf(s, "pin %d (%s): %s %s%s", pin,
- desc->name ? desc->name : "unnamed",
- desc->mux_owner ? desc->mux_owner
- : "(MUX UNCLAIMED)",
- desc->gpio_owner ? desc->gpio_owner
- : "(GPIO UNCLAIMED)",
- is_hog ? " (HOG)" : "");
+ scoped_guard(mutex, &desc->mux_lock) {
+ if (desc->mux_owner &&
+ !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
+ is_hog = true;
+
+ if (pmxops->strict) {
+ if (desc->mux_owner)
+ seq_printf(s, "pin %d (%s): device %s%s",
+ pin, desc->name, desc->mux_owner,
+ is_hog ? " (HOG)" : "");
+ else if (desc->gpio_owner)
+ seq_printf(s, "pin %d (%s): GPIO %s",
+ pin, desc->name, desc->gpio_owner);
+ else
+ seq_printf(s, "pin %d (%s): UNCLAIMED",
+ pin, desc->name);
+ } else {
+ /* For non-strict controllers */
+ seq_printf(s, "pin %d (%s): %s %s%s", pin, desc->name,
+ desc->mux_owner ? desc->mux_owner
+ : "(MUX UNCLAIMED)",
+ desc->gpio_owner ? desc->gpio_owner
+ : "(GPIO UNCLAIMED)",
+ is_hog ? " (HOG)" : "");
+ }
- if (desc->mux_setting)
- seq_printf(s, " function %s group %s\n",
- pmxops->get_function_name(pctldev,
- desc->mux_setting->func),
- pctlops->get_group_name(pctldev,
- desc->mux_setting->group));
- else
- seq_printf(s, "\n");
+ /* If mux: print function+group claiming the pin */
+ if (desc->mux_setting)
+ seq_printf(s, " function %s group %s\n",
+ pmxops->get_function_name(pctldev,
+ desc->mux_setting->func),
+ pctlops->get_group_name(pctldev,
+ desc->mux_setting->group));
+ else
+ seq_putc(s, '\n');
+ }
}
mutex_unlock(&pctldev->mutex);
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinmux_pins);
-void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map)
+void pinmux_show_map(struct seq_file *s, const struct pinctrl_map *map)
{
seq_printf(s, "group %s\nfunction %s\n",
map->data.mux.group ? map->data.mux.group : "(default)",
@@ -596,7 +694,7 @@ void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map)
}
void pinmux_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting)
+ const struct pinctrl_setting *setting)
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
@@ -609,37 +707,308 @@ void pinmux_show_setting(struct seq_file *s,
setting->data.mux.func);
}
-static int pinmux_functions_open(struct inode *inode, struct file *file)
+static int pinmux_select_show(struct seq_file *s, void *unused)
{
- return single_open(file, pinmux_functions_show, inode->i_private);
+ return -EPERM;
}
-static int pinmux_pins_open(struct inode *inode, struct file *file)
+static ssize_t pinmux_select_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
{
- return single_open(file, pinmux_pins_show, inode->i_private);
-}
+ struct seq_file *sfile = file->private_data;
+ struct pinctrl_dev *pctldev = sfile->private;
+ const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ const char *const *groups;
+ char *buf, *gname, *fname;
+ unsigned int num_groups;
+ int fsel, gsel, ret;
+
+ buf = memdup_user_nul(user_buf, len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* remove leading and trailing spaces of input buffer */
+ gname = strstrip(buf);
+ if (*gname == '\0') {
+ ret = -EINVAL;
+ goto exit_free_buf;
+ }
+
+ /* find a separator which is a spacelike character */
+ for (fname = gname; !isspace(*fname); fname++) {
+ if (*fname == '\0') {
+ ret = -EINVAL;
+ goto exit_free_buf;
+ }
+ }
+ *fname = '\0';
+
+ /* drop extra spaces between function and group names */
+ fname = skip_spaces(fname + 1);
+ if (*fname == '\0') {
+ ret = -EINVAL;
+ goto exit_free_buf;
+ }
-static const struct file_operations pinmux_functions_ops = {
- .open = pinmux_functions_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+ ret = pinmux_func_name_to_selector(pctldev, fname);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid function %s in map table\n", fname);
+ goto exit_free_buf;
+ }
+ fsel = ret;
+
+ ret = pmxops->get_function_groups(pctldev, fsel, &groups, &num_groups);
+ if (ret) {
+ dev_err(pctldev->dev, "no groups for function %d (%s)", fsel, fname);
+ goto exit_free_buf;
+ }
+
+ ret = match_string(groups, num_groups, gname);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid group %s", gname);
+ goto exit_free_buf;
+ }
+
+ ret = pinctrl_get_group_selector(pctldev, gname);
+ if (ret < 0)
+ goto exit_free_buf;
+ gsel = ret;
+
+ ret = pmxops->set_mux(pctldev, fsel, gsel);
+ if (ret) {
+ dev_err(pctldev->dev, "set_mux() failed: %d", ret);
+ goto exit_free_buf;
+ }
+ ret = len;
-static const struct file_operations pinmux_pins_ops = {
- .open = pinmux_pins_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+exit_free_buf:
+ kfree(buf);
+
+ return ret;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(pinmux_select);
void pinmux_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
- debugfs_create_file("pinmux-functions", S_IFREG | S_IRUGO,
- devroot, pctldev, &pinmux_functions_ops);
- debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO,
- devroot, pctldev, &pinmux_pins_ops);
+ debugfs_create_file("pinmux-functions", 0444,
+ devroot, pctldev, &pinmux_functions_fops);
+ debugfs_create_file("pinmux-pins", 0444,
+ devroot, pctldev, &pinmux_pins_fops);
+ debugfs_create_file("pinmux-select", 0200,
+ devroot, pctldev, &pinmux_select_fops);
}
#endif /* CONFIG_DEBUG_FS */
+
+#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
+
+/**
+ * pinmux_generic_get_function_count() - returns number of functions
+ * @pctldev: pin controller device
+ */
+int pinmux_generic_get_function_count(struct pinctrl_dev *pctldev)
+{
+ return pctldev->num_functions;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_get_function_count);
+
+/**
+ * pinmux_generic_get_function_name() - returns the function name
+ * @pctldev: pin controller device
+ * @selector: function number
+ */
+const char *
+pinmux_generic_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct function_desc *function;
+
+ function = radix_tree_lookup(&pctldev->pin_function_tree,
+ selector);
+ if (!function)
+ return NULL;
+
+ return function->func->name;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_get_function_name);
+
+/**
+ * pinmux_generic_get_function_groups() - gets the function groups
+ * @pctldev: pin controller device
+ * @selector: function number
+ * @groups: array of pin groups
+ * @ngroups: number of pin groups
+ */
+int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int * const ngroups)
+{
+ struct function_desc *function;
+
+ function = radix_tree_lookup(&pctldev->pin_function_tree,
+ selector);
+ if (!function) {
+ dev_err(pctldev->dev, "%s could not find function%i\n",
+ __func__, selector);
+ return -EINVAL;
+ }
+ *groups = function->func->groups;
+ *ngroups = function->func->ngroups;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_get_function_groups);
+
+/**
+ * pinmux_generic_get_function() - returns a function based on the number
+ * @pctldev: pin controller device
+ * @selector: function number
+ */
+const struct function_desc *
+pinmux_generic_get_function(struct pinctrl_dev *pctldev, unsigned int selector)
+{
+ struct function_desc *function;
+
+ function = radix_tree_lookup(&pctldev->pin_function_tree,
+ selector);
+ if (!function)
+ return NULL;
+
+ return function;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_get_function);
+
+/**
+ * pinmux_generic_function_is_gpio() - returns true if given function is a GPIO
+ * @pctldev: pin controller device
+ * @selector: function number
+ *
+ * Returns:
+ * True if given function is a GPIO, false otherwise.
+ */
+bool pinmux_generic_function_is_gpio(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct function_desc *function;
+
+ function = radix_tree_lookup(&pctldev->pin_function_tree, selector);
+ if (!function)
+ return false;
+
+ return function->func->flags & PINFUNCTION_FLAG_GPIO;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_function_is_gpio);
+
+/**
+ * pinmux_generic_add_function() - adds a function group
+ * @pctldev: pin controller device
+ * @name: name of the function
+ * @groups: array of pin groups
+ * @ngroups: number of pin groups
+ * @data: pin controller driver specific data
+ */
+int pinmux_generic_add_function(struct pinctrl_dev *pctldev,
+ const char *name,
+ const char * const *groups,
+ const unsigned int ngroups,
+ void *data)
+{
+ struct pinfunction func = PINCTRL_PINFUNCTION(name, groups, ngroups);
+
+ return pinmux_generic_add_pinfunction(pctldev, &func, data);
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_add_function);
+
+/**
+ * pinmux_generic_add_pinfunction() - adds a function group
+ * @pctldev: pin controller device
+ * @func: pinfunction structure describing the function group
+ * @data: pin controller driver specific data
+ */
+int pinmux_generic_add_pinfunction(struct pinctrl_dev *pctldev,
+ const struct pinfunction *func, void *data)
+{
+ struct function_desc *function;
+ int selector, error;
+
+ selector = pinmux_func_name_to_selector(pctldev, func->name);
+ if (selector >= 0)
+ return selector;
+
+ selector = pctldev->num_functions;
+
+ function = devm_kzalloc(pctldev->dev, sizeof(*function), GFP_KERNEL);
+ if (!function)
+ return -ENOMEM;
+
+ /*
+ * FIXME: It's generally a bad idea to use devres in subsystem core
+ * code - managed interfaces are aimed at drivers - but pinctrl already
+ * uses it all over the place so it's a larger piece of technical debt
+ * to fix.
+ */
+ function->func = devm_kmemdup_const(pctldev->dev, func,
+ sizeof(*func), GFP_KERNEL);
+ if (!function->func)
+ return -ENOMEM;
+
+ function->data = data;
+
+ error = radix_tree_insert(&pctldev->pin_function_tree, selector, function);
+ if (error)
+ return error;
+
+ pctldev->num_functions++;
+
+ return selector;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_add_pinfunction);
+
+/**
+ * pinmux_generic_remove_function() - removes a numbered function
+ * @pctldev: pin controller device
+ * @selector: function number
+ *
+ * Note that the caller must take care of locking.
+ */
+int pinmux_generic_remove_function(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ struct function_desc *function;
+
+ function = radix_tree_lookup(&pctldev->pin_function_tree,
+ selector);
+ if (!function)
+ return -ENOENT;
+
+ radix_tree_delete(&pctldev->pin_function_tree, selector);
+ devm_kfree(pctldev->dev, function);
+
+ pctldev->num_functions--;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinmux_generic_remove_function);
+
+/**
+ * pinmux_generic_free_functions() - removes all functions
+ * @pctldev: pin controller device
+ *
+ * Note that the caller must take care of locking. The pinctrl
+ * functions are allocated with devm_kzalloc() so no need to free
+ * them here.
+ */
+void pinmux_generic_free_functions(struct pinctrl_dev *pctldev)
+{
+ struct radix_tree_iter iter;
+ void __rcu **slot;
+
+ radix_tree_for_each_slot(slot, &pctldev->pin_function_tree, &iter, 0)
+ radix_tree_delete(&pctldev->pin_function_tree, iter.index);
+
+ pctldev->num_functions = 0;
+}
+
+#endif /* CONFIG_GENERIC_PINMUX_FUNCTIONS */