diff options
-rw-r--r-- | drivers/pinctrl/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pinctrl/core.c | 4 | ||||
-rw-r--r-- | drivers/pinctrl/core.h | 6 | ||||
-rw-r--r-- | drivers/pinctrl/pinmux.c | 173 | ||||
-rw-r--r-- | drivers/pinctrl/pinmux.h | 56 |
5 files changed, 243 insertions, 0 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index add257f80d76..7e964284b2c9 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -14,6 +14,10 @@ config GENERIC_PINCTRL_GROUPS config PINMUX bool "Support pin multiplexing controllers" if COMPILE_TEST +config GENERIC_PINMUX_FUNCTIONS + bool + select PINMUX + config PINCONF bool "Support pin configuration controllers" if COMPILE_TEST diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 736149d3a33d..d311d73852a0 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1996,6 +1996,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, #ifdef CONFIG_GENERIC_PINCTRL_GROUPS INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL); #endif +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS + INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL); +#endif INIT_LIST_HEAD(&pctldev->gpio_ranges); INIT_DELAYED_WORK(&pctldev->late_init, pinctrl_late_init); pctldev->dev = dev; @@ -2076,6 +2079,7 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) mutex_lock(&pctldev->mutex); /* TODO: check that no pinmuxes are still active? */ list_del(&pctldev->node); + pinmux_generic_free_functions(pctldev); pinctrl_generic_free_groups(pctldev); /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index b04c59bf9701..ad812a2d7248 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -26,6 +26,8 @@ struct pinctrl_gpio_range; * this radix tree * @pin_group_tree: optionally each pin group can be stored in this radix tree * @num_groups: optionally number of groups can be kept here + * @pin_function_tree: optionally each function can be stored in this radix tree + * @num_functions: optionally number of functions can be kept here * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, * ranges are added to this list at runtime * @dev: the device entry for this pin controller @@ -47,6 +49,10 @@ struct pinctrl_dev { struct radix_tree_root pin_group_tree; unsigned int num_groups; #endif +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS + struct radix_tree_root pin_function_tree; + unsigned int num_functions; +#endif struct list_head gpio_ranges; struct device *dev; struct module *owner; diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 9373146f3afc..29ad3151abec 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -682,3 +682,176 @@ void pinmux_init_device_debugfs(struct dentry *devroot, } #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->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 + * @num_groups: 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) +{ + 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->group_names; + *num_groups = function->num_group_names; + + 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 + * @group_selector: function number + */ +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_get_function_groups() - gets the function groups + * @pctldev: pin controller device + * @name: name of the function + * @groups: array of pin groups + * @num_groups: 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, + void *data) +{ + struct function_desc *function; + + 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; + function->data = data; + + radix_tree_insert(&pctldev->pin_function_tree, pctldev->num_functions, + function); + + pctldev->num_functions++; + + return 0; +} +EXPORT_SYMBOL_GPL(pinmux_generic_add_function); + +/** + * 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. + */ +void pinmux_generic_free_functions(struct pinctrl_dev *pctldev) +{ + struct radix_tree_iter iter; + struct function_desc *function; + unsigned long *indices; + void **slot; + int i = 0; + + indices = devm_kzalloc(pctldev->dev, sizeof(*indices) * + pctldev->num_functions, GFP_KERNEL); + if (!indices) + return; + + radix_tree_for_each_slot(slot, &pctldev->pin_function_tree, &iter, 0) + indices[i++] = iter.index; + + for (i = 0; i < pctldev->num_functions; i++) { + function = radix_tree_lookup(&pctldev->pin_function_tree, + indices[i]); + radix_tree_delete(&pctldev->pin_function_tree, indices[i]); + devm_kfree(pctldev->dev, function); + } + + pctldev->num_functions = 0; +} + +#endif /* CONFIG_GENERIC_PINMUX_FUNCTIONS */ diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index d1a98b1c9fce..248d8ea30e26 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -111,3 +111,59 @@ static inline void pinmux_init_device_debugfs(struct dentry *devroot, } #endif + +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS + +/** + * struct function_desc - generic function descriptor + * @name: name of the function + * @group_names: array of pin group names + * @num_group_names: number of pin group names + * @data: pin controller driver specific data + */ +struct function_desc { + const char *name; + const char **group_names; + int num_group_names; + void *data; +}; + +int pinmux_generic_get_function_count(struct pinctrl_dev *pctldev); + +const char * +pinmux_generic_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector); + +int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned * const num_groups); + +struct function_desc *pinmux_generic_get_function(struct pinctrl_dev *pctldev, + unsigned int selector); + +int pinmux_generic_add_function(struct pinctrl_dev *pctldev, + const char *name, + const char **groups, + unsigned const num_groups, + void *data); + +int pinmux_generic_remove_function(struct pinctrl_dev *pctldev, + unsigned int selector); + +static inline int +pinmux_generic_remove_last_function(struct pinctrl_dev *pctldev) +{ + return pinmux_generic_remove_function(pctldev, + pctldev->num_functions - 1); +} + +void pinmux_generic_free_functions(struct pinctrl_dev *pctldev); + +#else + +static inline void pinmux_generic_free_functions(struct pinctrl_dev *pctldev) +{ +} + +#endif /* CONFIG_GENERIC_PINMUX_FUNCTIONS */ |