Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / pinctrl / berlin / berlin.c
diff --git a/kernel/drivers/pinctrl/berlin/berlin.c b/kernel/drivers/pinctrl/berlin/berlin.c
new file mode 100644 (file)
index 0000000..7f0b0f9
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Marvell Berlin SoC pinctrl core driver
+ *
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+#include "berlin.h"
+
+struct berlin_pinctrl {
+       struct regmap *regmap;
+       struct device *dev;
+       const struct berlin_pinctrl_desc *desc;
+       struct berlin_pinctrl_function *functions;
+       unsigned nfunctions;
+       struct pinctrl_dev *pctrl_dev;
+};
+
+static int berlin_pinctrl_get_group_count(struct pinctrl_dev *pctrl_dev)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+       return pctrl->desc->ngroups;
+}
+
+static const char *berlin_pinctrl_get_group_name(struct pinctrl_dev *pctrl_dev,
+                                                unsigned group)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+       return pctrl->desc->groups[group].name;
+}
+
+static int berlin_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrl_dev,
+                                        struct device_node *node,
+                                        struct pinctrl_map **map,
+                                        unsigned *num_maps)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+       struct property *prop;
+       const char *function_name, *group_name;
+       unsigned reserved_maps = 0;
+       int ret, ngroups;
+
+       *map = NULL;
+       *num_maps = 0;
+
+       ret = of_property_read_string(node, "function", &function_name);
+       if (ret) {
+               dev_err(pctrl->dev,
+                       "missing function property in node %s\n",
+                       node->name);
+               return -EINVAL;
+       }
+
+       ngroups = of_property_count_strings(node, "groups");
+       if (ngroups < 0) {
+               dev_err(pctrl->dev,
+                       "missing groups property in node %s\n",
+                       node->name);
+               return -EINVAL;
+       }
+
+       ret = pinctrl_utils_reserve_map(pctrl_dev, map, &reserved_maps,
+                                       num_maps, ngroups);
+       if (ret) {
+               dev_err(pctrl->dev, "can't reserve map: %d\n", ret);
+               return ret;
+       }
+
+       of_property_for_each_string(node, "groups", prop, group_name) {
+               ret = pinctrl_utils_add_map_mux(pctrl_dev, map, &reserved_maps,
+                                               num_maps, group_name,
+                                               function_name);
+               if (ret) {
+                       dev_err(pctrl->dev, "can't add map: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct pinctrl_ops berlin_pinctrl_ops = {
+       .get_groups_count       = &berlin_pinctrl_get_group_count,
+       .get_group_name         = &berlin_pinctrl_get_group_name,
+       .dt_node_to_map         = &berlin_pinctrl_dt_node_to_map,
+       .dt_free_map            = &pinctrl_utils_dt_free_map,
+};
+
+static int berlin_pinmux_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+       return pctrl->nfunctions;
+}
+
+static const char *berlin_pinmux_get_function_name(struct pinctrl_dev *pctrl_dev,
+                                                  unsigned function)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+       return pctrl->functions[function].name;
+}
+
+static int berlin_pinmux_get_function_groups(struct pinctrl_dev *pctrl_dev,
+                                            unsigned function,
+                                            const char * const **groups,
+                                            unsigned * const num_groups)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+       *groups = pctrl->functions[function].groups;
+       *num_groups = pctrl->functions[function].ngroups;
+
+       return 0;
+}
+
+static struct berlin_desc_function *
+berlin_pinctrl_find_function_by_name(struct berlin_pinctrl *pctrl,
+                                    const struct berlin_desc_group *group,
+                                    const char *fname)
+{
+       struct berlin_desc_function *function = group->functions;
+
+       while (function->name) {
+               if (!strcmp(function->name, fname))
+                       return function;
+
+               function++;
+       }
+
+       return NULL;
+}
+
+static int berlin_pinmux_set(struct pinctrl_dev *pctrl_dev,
+                            unsigned function,
+                            unsigned group)
+{
+       struct berlin_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+       const struct berlin_desc_group *group_desc = pctrl->desc->groups + group;
+       struct berlin_pinctrl_function *func = pctrl->functions + function;
+       struct berlin_desc_function *function_desc =
+               berlin_pinctrl_find_function_by_name(pctrl, group_desc,
+                                                    func->name);
+       u32 mask, val;
+
+       if (!function_desc)
+               return -EINVAL;
+
+       mask = GENMASK(group_desc->lsb + group_desc->bit_width - 1,
+                      group_desc->lsb);
+       val = function_desc->muxval << group_desc->lsb;
+       regmap_update_bits(pctrl->regmap, group_desc->offset, mask, val);
+
+       return 0;
+}
+
+static const struct pinmux_ops berlin_pinmux_ops = {
+       .get_functions_count    = &berlin_pinmux_get_functions_count,
+       .get_function_name      = &berlin_pinmux_get_function_name,
+       .get_function_groups    = &berlin_pinmux_get_function_groups,
+       .set_mux                = &berlin_pinmux_set,
+};
+
+static int berlin_pinctrl_add_function(struct berlin_pinctrl *pctrl,
+                                      const char *name)
+{
+       struct berlin_pinctrl_function *function = pctrl->functions;
+
+       while (function->name) {
+               if (!strcmp(function->name, name)) {
+                       function->ngroups++;
+                       return -EEXIST;
+               }
+               function++;
+       }
+
+       function->name = name;
+       function->ngroups = 1;
+
+       pctrl->nfunctions++;
+
+       return 0;
+}
+
+static int berlin_pinctrl_build_state(struct platform_device *pdev)
+{
+       struct berlin_pinctrl *pctrl = platform_get_drvdata(pdev);
+       struct berlin_desc_group const *desc_group;
+       struct berlin_desc_function const *desc_function;
+       int i, max_functions = 0;
+
+       pctrl->nfunctions = 0;
+
+       for (i = 0; i < pctrl->desc->ngroups; i++) {
+               desc_group = pctrl->desc->groups + i;
+               /* compute the maxiumum number of functions a group can have */
+               max_functions += 1 << (desc_group->bit_width + 1);
+       }
+
+       /* we will reallocate later */
+       pctrl->functions = devm_kzalloc(&pdev->dev,
+                                       max_functions * sizeof(*pctrl->functions),
+                                       GFP_KERNEL);
+       if (!pctrl->functions)
+               return -ENOMEM;
+
+       /* register all functions */
+       for (i = 0; i < pctrl->desc->ngroups; i++) {
+               desc_group = pctrl->desc->groups + i;
+               desc_function = desc_group->functions;
+
+               while (desc_function->name) {
+                       berlin_pinctrl_add_function(pctrl, desc_function->name);
+                       desc_function++;
+               }
+       }
+
+       pctrl->functions = krealloc(pctrl->functions,
+                                   pctrl->nfunctions * sizeof(*pctrl->functions),
+                                   GFP_KERNEL);
+
+       /* map functions to theirs groups */
+       for (i = 0; i < pctrl->desc->ngroups; i++) {
+               desc_group = pctrl->desc->groups + i;
+               desc_function = desc_group->functions;
+
+               while (desc_function->name) {
+                       struct berlin_pinctrl_function
+                               *function = pctrl->functions;
+                       const char **groups;
+                       bool found = false;
+
+                       while (function->name) {
+                               if (!strcmp(desc_function->name, function->name)) {
+                                       found = true;
+                                       break;
+                               }
+                               function++;
+                       }
+
+                       if (!found)
+                               return -EINVAL;
+
+                       if (!function->groups) {
+                               function->groups =
+                                       devm_kzalloc(&pdev->dev,
+                                                    function->ngroups * sizeof(char *),
+                                                    GFP_KERNEL);
+
+                               if (!function->groups)
+                                       return -ENOMEM;
+                       }
+
+                       groups = function->groups;
+                       while (*groups)
+                               groups++;
+
+                       *groups = desc_group->name;
+
+                       desc_function++;
+               }
+       }
+
+       return 0;
+}
+
+static struct pinctrl_desc berlin_pctrl_desc = {
+       .name           = "berlin-pinctrl",
+       .pctlops        = &berlin_pinctrl_ops,
+       .pmxops         = &berlin_pinmux_ops,
+       .owner          = THIS_MODULE,
+};
+
+int berlin_pinctrl_probe(struct platform_device *pdev,
+                        const struct berlin_pinctrl_desc *desc)
+{
+       struct device *dev = &pdev->dev;
+       struct berlin_pinctrl *pctrl;
+       struct regmap *regmap;
+       int ret;
+
+       regmap = dev_get_regmap(&pdev->dev, NULL);
+       if (!regmap)
+               return -ENODEV;
+
+       pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
+       if (!pctrl)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, pctrl);
+
+       pctrl->regmap = regmap;
+       pctrl->dev = &pdev->dev;
+       pctrl->desc = desc;
+
+       ret = berlin_pinctrl_build_state(pdev);
+       if (ret) {
+               dev_err(dev, "cannot build driver state: %d\n", ret);
+               return ret;
+       }
+
+       pctrl->pctrl_dev = pinctrl_register(&berlin_pctrl_desc, dev, pctrl);
+       if (!pctrl->pctrl_dev) {
+               dev_err(dev, "failed to register pinctrl driver\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}