diff options
Diffstat (limited to 'drivers/pinctrl/pinmux.c')
| -rw-r--r-- | drivers/pinctrl/pinmux.c | 693 |
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 */ |
