pinctrl: core: Add generic pinctrl functions for managing groups
authorTony Lindgren <tony@atomide.com>
Tue, 27 Dec 2016 17:20:01 +0000 (09:20 -0800)
committerLinus Walleij <linus.walleij@linaro.org>
Tue, 3 Jan 2017 08:26:18 +0000 (09:26 +0100)
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: Tony Lindgren <tony@atomide.com>
[Renamed the Kconfig item and moved things around]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/Kconfig
drivers/pinctrl/core.c
drivers/pinctrl/core.h
drivers/pinctrl/pinmux.c
drivers/pinctrl/pinmux.h

index add257f..7e96428 100644 (file)
@@ -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
 
index 736149d..d311d73 100644 (file)
@@ -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,
index b04c59b..ad812a2 100644 (file)
@@ -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;
index 9373146..29ad315 100644 (file)
@@ -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 */
index d1a98b1..248d8ea 100644 (file)
@@ -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 */