|
@@ -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 */
|