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.c70
1 files changed, 60 insertions, 10 deletions
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 79814758a084..3a8dd184ba3d 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -89,13 +89,20 @@ 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 (ops->strict && desc->mux_usecount)
+ 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);
@@ -116,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) {
@@ -126,11 +135,21 @@ 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);
scoped_guard(mutex, &desc->mux_lock) {
- if ((!gpio_range || ops->strict) &&
+ 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",
@@ -138,7 +157,7 @@ static int pin_request(struct pinctrl_dev *pctldev,
goto out;
}
- if ((gpio_range || ops->strict) && desc->gpio_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->gpio_owner, owner);
@@ -337,7 +356,7 @@ static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev,
while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev, selector);
- if (!strcmp(function, fname))
+ if (fname && !strcmp(function, fname))
return selector;
selector++;
@@ -810,7 +829,7 @@ pinmux_generic_get_function_name(struct pinctrl_dev *pctldev,
if (!function)
return NULL;
- return function->func.name;
+ return function->func->name;
}
EXPORT_SYMBOL_GPL(pinmux_generic_get_function_name);
@@ -835,8 +854,8 @@ int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev,
__func__, selector);
return -EINVAL;
}
- *groups = function->func.groups;
- *ngroups = function->func.ngroups;
+ *groups = function->func->groups;
+ *ngroups = function->func->ngroups;
return 0;
}
@@ -847,8 +866,8 @@ EXPORT_SYMBOL_GPL(pinmux_generic_get_function_groups);
* @pctldev: pin controller device
* @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;
@@ -862,6 +881,27 @@ 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
@@ -903,7 +943,17 @@ int pinmux_generic_add_pinfunction(struct pinctrl_dev *pctldev,
if (!function)
return -ENOMEM;
- function->func = *func;
+ /*
+ * 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);