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.c506
1 files changed, 338 insertions, 168 deletions
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 16b3ae5e4f44..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,33 +9,35 @@
* 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 ||
@@ -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 || ops->strict) &&
- 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->mux_owner, owner);
- goto out;
- }
+ 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) && desc->gpio_owner) {
- dev_err(pctldev->dev,
- "pin %s already requested by %s; cannot claim for %s\n",
- desc->name, desc->gpio_owner, owner);
- goto out;
- }
+ 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->mux_owner, owner);
+ goto out;
+ }
- if (gpio_range) {
- desc->gpio_owner = owner;
- } else {
- desc->mux_usecount++;
- if (desc->mux_usecount > 1)
- return 0;
+ 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->gpio_owner, owner);
+ goto out;
+ }
+
+ 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;
@@ -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,30 +349,29 @@ 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);
- if (!strcmp(function, fname))
+ if (fname && !strcmp(function, fname))
return selector;
selector++;
}
- dev_err(pctldev->dev, "function '%s' not supported\n", 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;
@@ -372,19 +425,19 @@ 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 = 0;
- const unsigned *pins = NULL;
- unsigned num_pins = 0;
+ const unsigned int *pins = NULL;
+ unsigned int num_pins = 0;
int i;
struct pin_desc *desc;
@@ -415,9 +468,8 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting)
pname = desc ? desc->name : "non-existing";
gname = pctlops->get_group_name(pctldev,
setting->data.mux.group);
- dev_err(pctldev->dev,
- "could not request pin %d (%s) from group %s "
- " on device %s\n",
+ 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;
@@ -433,7 +485,8 @@ 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->set_mux(pctldev, setting->data.mux.func,
@@ -447,8 +500,10 @@ int pinmux_enable_setting(struct pinctrl_setting const *setting)
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 */
@@ -458,15 +513,16 @@ 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;
int ret = 0;
- const unsigned *pins = NULL;
- unsigned num_pins = 0;
+ const unsigned int *pins = NULL;
+ unsigned int num_pins = 0;
int i;
struct pin_desc *desc;
+ bool is_equal;
if (pctlops->get_group_pins)
ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
@@ -492,9 +548,10 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting)
pins[i]);
continue;
}
- if (desc->mux_setting == &(setting->data.mux)) {
- desc->mux_setting = NULL;
- /* And release the pin */
+ scoped_guard(mutex, &desc->mux_lock)
+ is_equal = (desc->mux_setting == &(setting->data.mux));
+
+ if (is_equal) {
pin_free(pctldev, pins[i], NULL);
} else {
const char *gname;
@@ -502,9 +559,7 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting)
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",
+ "not freeing pin %d (%s) as part of deactivating group %s - it is already used for some other setting",
pins[i], desc->name, gname);
}
}
@@ -517,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;
@@ -529,7 +584,7 @@ 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;
@@ -542,7 +597,7 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
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");
@@ -554,13 +609,14 @@ 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;
@@ -586,48 +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;
-
- if (pmxops->strict) {
- if (desc->mux_owner)
- seq_printf(s, "pin %d (%s): device %s%s",
- pin, desc->name, desc->mux_owner,
+ 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)" : "");
- else if (desc->gpio_owner)
- seq_printf(s, "pin %d (%s): GPIO %s",
- pin, desc->name, desc->gpio_owner);
+ }
+
+ /* 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_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)" : "");
+ seq_putc(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_printf(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)",
@@ -635,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;
@@ -648,37 +707,96 @@ 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;
+ }
+
+ 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;
-static const struct file_operations pinmux_functions_ops = {
- .open = pinmux_functions_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+ 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 */
@@ -711,7 +829,7 @@ pinmux_generic_get_function_name(struct pinctrl_dev *pctldev,
if (!function)
return NULL;
- return function->name;
+ return function->func->name;
}
EXPORT_SYMBOL_GPL(pinmux_generic_get_function_name);
@@ -720,12 +838,12 @@ EXPORT_SYMBOL_GPL(pinmux_generic_get_function_name);
* @pctldev: pin controller device
* @selector: function number
* @groups: array of pin groups
- * @num_groups: number 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 * const num_groups)
+ unsigned int * const ngroups)
{
struct function_desc *function;
@@ -736,8 +854,8 @@ int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev,
__func__, selector);
return -EINVAL;
}
- *groups = function->group_names;
- *num_groups = function->num_group_names;
+ *groups = function->func->groups;
+ *ngroups = function->func->ngroups;
return 0;
}
@@ -746,10 +864,10 @@ EXPORT_SYMBOL_GPL(pinmux_generic_get_function_groups);
/**
* pinmux_generic_get_function() - returns a function based on the number
* @pctldev: pin controller device
- * @group_selector: function number
+ * @selector: function number
*/
-struct function_desc *pinmux_generic_get_function(struct pinctrl_dev *pctldev,
- unsigned int selector)
+const struct function_desc *
+pinmux_generic_get_function(struct pinctrl_dev *pctldev, unsigned int selector)
{
struct function_desc *function;
@@ -763,38 +881,90 @@ struct function_desc *pinmux_generic_get_function(struct pinctrl_dev *pctldev,
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
- * @num_groups: number 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 **groups,
- const unsigned int num_groups,
+ 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;
- function->name = name;
- function->group_names = groups;
- function->num_group_names = num_groups;
+ /*
+ * 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;
- radix_tree_insert(&pctldev->pin_function_tree, pctldev->num_functions,
- function);
+ error = radix_tree_insert(&pctldev->pin_function_tree, selector, function);
+ if (error)
+ return error;
pctldev->num_functions++;
- return 0;
+ return selector;
}
-EXPORT_SYMBOL_GPL(pinmux_generic_add_function);
+EXPORT_SYMBOL_GPL(pinmux_generic_add_pinfunction);
/**
* pinmux_generic_remove_function() - removes a numbered function
@@ -833,7 +1003,7 @@ EXPORT_SYMBOL_GPL(pinmux_generic_remove_function);
void pinmux_generic_free_functions(struct pinctrl_dev *pctldev)
{
struct radix_tree_iter iter;
- void **slot;
+ void __rcu **slot;
radix_tree_for_each_slot(slot, &pctldev->pin_function_tree, &iter, 0)
radix_tree_delete(&pctldev->pin_function_tree, iter.index);