Commit a76edc89 authored by Tony Lindgren's avatar Tony Lindgren Committed by Linus Walleij

pinctrl: core: Add generic pinctrl functions for managing groups

We can add generic helpers for function handling for cases where the pin
controller driver does not need to use static arrays.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
[Renamed the Kconfig item and moved things around]
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent c033a718
......@@ -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
......
......@@ -1995,6 +1995,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
#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);
......@@ -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,
......
......@@ -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
......@@ -46,6 +48,10 @@ struct pinctrl_dev {
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
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;
......
......@@ -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 */
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment