These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / pinctrl / uniphier / pinctrl-uniphier-core.c
diff --git a/kernel/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/kernel/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
new file mode 100644 (file)
index 0000000..589872c
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+#include "pinctrl-uniphier.h"
+
+struct uniphier_pinctrl_priv {
+       struct pinctrl_dev *pctldev;
+       struct regmap *regmap;
+       struct uniphier_pinctrl_socdata *socdata;
+};
+
+static int uniphier_pctl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       return priv->socdata->groups_count;
+}
+
+static const char *uniphier_pctl_get_group_name(struct pinctrl_dev *pctldev,
+                                               unsigned selector)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       return priv->socdata->groups[selector].name;
+}
+
+static int uniphier_pctl_get_group_pins(struct pinctrl_dev *pctldev,
+                                       unsigned selector,
+                                       const unsigned **pins,
+                                       unsigned *num_pins)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       *pins = priv->socdata->groups[selector].pins;
+       *num_pins = priv->socdata->groups[selector].num_pins;
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
+                                      struct seq_file *s, unsigned offset)
+{
+       const struct pinctrl_pin_desc *pin = &pctldev->desc->pins[offset];
+       const char *pull_dir, *drv_str;
+
+       switch (uniphier_pin_get_pull_dir(pin->drv_data)) {
+       case UNIPHIER_PIN_PULL_UP:
+               pull_dir = "UP";
+               break;
+       case UNIPHIER_PIN_PULL_DOWN:
+               pull_dir = "DOWN";
+               break;
+       case UNIPHIER_PIN_PULL_NONE:
+               pull_dir = "NONE";
+               break;
+       default:
+               BUG();
+       }
+
+       switch (uniphier_pin_get_drv_str(pin->drv_data)) {
+       case UNIPHIER_PIN_DRV_4_8:
+               drv_str = "4/8(mA)";
+               break;
+       case UNIPHIER_PIN_DRV_8_12_16_20:
+               drv_str = "8/12/16/20(mA)";
+               break;
+       case UNIPHIER_PIN_DRV_FIXED_4:
+               drv_str = "4(mA)";
+               break;
+       case UNIPHIER_PIN_DRV_FIXED_5:
+               drv_str = "5(mA)";
+               break;
+       case UNIPHIER_PIN_DRV_FIXED_8:
+               drv_str = "8(mA)";
+               break;
+       case UNIPHIER_PIN_DRV_NONE:
+               drv_str = "NONE";
+               break;
+       default:
+               BUG();
+       }
+
+       seq_printf(s, " PULL_DIR=%s  DRV_STR=%s", pull_dir, drv_str);
+}
+#endif
+
+static const struct pinctrl_ops uniphier_pctlops = {
+       .get_groups_count = uniphier_pctl_get_groups_count,
+       .get_group_name = uniphier_pctl_get_group_name,
+       .get_group_pins = uniphier_pctl_get_group_pins,
+#ifdef CONFIG_DEBUG_FS
+       .pin_dbg_show = uniphier_pctl_pin_dbg_show,
+#endif
+       .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+       .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int uniphier_conf_pin_bias_get(struct pinctrl_dev *pctldev,
+                                     const struct pinctrl_pin_desc *pin,
+                                     enum pin_config_param param)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       enum uniphier_pin_pull_dir pull_dir =
+                               uniphier_pin_get_pull_dir(pin->drv_data);
+       unsigned int pupdctrl, reg, shift, val;
+       unsigned int expected = 1;
+       int ret;
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               if (pull_dir == UNIPHIER_PIN_PULL_NONE)
+                       return 0;
+               if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED ||
+                   pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED)
+                       return -EINVAL;
+               expected = 0;
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED)
+                       return 0;
+               if (pull_dir != UNIPHIER_PIN_PULL_UP)
+                       return -EINVAL;
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED)
+                       return 0;
+               if (pull_dir != UNIPHIER_PIN_PULL_DOWN)
+                       return -EINVAL;
+               break;
+       default:
+               BUG();
+       }
+
+       pupdctrl = uniphier_pin_get_pupdctrl(pin->drv_data);
+
+       reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4;
+       shift = pupdctrl % 32;
+
+       ret = regmap_read(priv->regmap, reg, &val);
+       if (ret)
+               return ret;
+
+       val = (val >> shift) & 1;
+
+       return (val == expected) ? 0 : -EINVAL;
+}
+
+static int uniphier_conf_pin_drive_get(struct pinctrl_dev *pctldev,
+                                      const struct pinctrl_pin_desc *pin,
+                                      u16 *strength)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       enum uniphier_pin_drv_str drv_str =
+                               uniphier_pin_get_drv_str(pin->drv_data);
+       const unsigned int strength_4_8[] = {4, 8};
+       const unsigned int strength_8_12_16_20[] = {8, 12, 16, 20};
+       const unsigned int *supported_strength;
+       unsigned int drvctrl, reg, shift, mask, width, val;
+       int ret;
+
+       switch (drv_str) {
+       case UNIPHIER_PIN_DRV_4_8:
+               supported_strength = strength_4_8;
+               width = 1;
+               break;
+       case UNIPHIER_PIN_DRV_8_12_16_20:
+               supported_strength = strength_8_12_16_20;
+               width = 2;
+               break;
+       case UNIPHIER_PIN_DRV_FIXED_4:
+               *strength = 4;
+               return 0;
+       case UNIPHIER_PIN_DRV_FIXED_5:
+               *strength = 5;
+               return 0;
+       case UNIPHIER_PIN_DRV_FIXED_8:
+               *strength = 8;
+               return 0;
+       default:
+               /* drive strength control is not supported for this pin */
+               return -EINVAL;
+       }
+
+       drvctrl = uniphier_pin_get_drvctrl(pin->drv_data);
+       drvctrl *= width;
+
+       reg = (width == 2) ? UNIPHIER_PINCTRL_DRV2CTRL_BASE :
+                            UNIPHIER_PINCTRL_DRVCTRL_BASE;
+
+       reg += drvctrl / 32 * 4;
+       shift = drvctrl % 32;
+       mask = (1U << width) - 1;
+
+       ret = regmap_read(priv->regmap, reg, &val);
+       if (ret)
+               return ret;
+
+       *strength = supported_strength[(val >> shift) & mask];
+
+       return 0;
+}
+
+static int uniphier_conf_pin_input_enable_get(struct pinctrl_dev *pctldev,
+                                       const struct pinctrl_pin_desc *pin)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       unsigned int iectrl = uniphier_pin_get_iectrl(pin->drv_data);
+       unsigned int val;
+       int ret;
+
+       if (iectrl == UNIPHIER_PIN_IECTRL_NONE)
+               /* This pin is always input-enabled. */
+               return 0;
+
+       ret = regmap_read(priv->regmap, UNIPHIER_PINCTRL_IECTRL, &val);
+       if (ret)
+               return ret;
+
+       return val & BIT(iectrl) ? 0 : -EINVAL;
+}
+
+static int uniphier_conf_pin_config_get(struct pinctrl_dev *pctldev,
+                                       unsigned pin,
+                                       unsigned long *configs)
+{
+       const struct pinctrl_pin_desc *pin_desc = &pctldev->desc->pins[pin];
+       enum pin_config_param param = pinconf_to_config_param(*configs);
+       bool has_arg = false;
+       u16 arg;
+       int ret;
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+       case PIN_CONFIG_BIAS_PULL_UP:
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               ret = uniphier_conf_pin_bias_get(pctldev, pin_desc, param);
+               break;
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               ret = uniphier_conf_pin_drive_get(pctldev, pin_desc, &arg);
+               has_arg = true;
+               break;
+       case PIN_CONFIG_INPUT_ENABLE:
+               ret = uniphier_conf_pin_input_enable_get(pctldev, pin_desc);
+               break;
+       default:
+               /* unsupported parameter */
+               ret = -EINVAL;
+               break;
+       }
+
+       if (ret == 0 && has_arg)
+               *configs = pinconf_to_config_packed(param, arg);
+
+       return ret;
+}
+
+static int uniphier_conf_pin_bias_set(struct pinctrl_dev *pctldev,
+                                     const struct pinctrl_pin_desc *pin,
+                                     enum pin_config_param param,
+                                     u16 arg)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       enum uniphier_pin_pull_dir pull_dir =
+                               uniphier_pin_get_pull_dir(pin->drv_data);
+       unsigned int pupdctrl, reg, shift;
+       unsigned int val = 1;
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               if (pull_dir == UNIPHIER_PIN_PULL_NONE)
+                       return 0;
+               if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED ||
+                   pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) {
+                       dev_err(pctldev->dev,
+                               "can not disable pull register for pin %u (%s)\n",
+                               pin->number, pin->name);
+                       return -EINVAL;
+               }
+               val = 0;
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED && arg != 0)
+                       return 0;
+               if (pull_dir != UNIPHIER_PIN_PULL_UP) {
+                       dev_err(pctldev->dev,
+                               "pull-up is unsupported for pin %u (%s)\n",
+                               pin->number, pin->name);
+                       return -EINVAL;
+               }
+               if (arg == 0) {
+                       dev_err(pctldev->dev, "pull-up can not be total\n");
+                       return -EINVAL;
+               }
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED && arg != 0)
+                       return 0;
+               if (pull_dir != UNIPHIER_PIN_PULL_DOWN) {
+                       dev_err(pctldev->dev,
+                               "pull-down is unsupported for pin %u (%s)\n",
+                               pin->number, pin->name);
+                       return -EINVAL;
+               }
+               if (arg == 0) {
+                       dev_err(pctldev->dev, "pull-down can not be total\n");
+                       return -EINVAL;
+               }
+               break;
+       case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+               if (pull_dir == UNIPHIER_PIN_PULL_NONE) {
+                       dev_err(pctldev->dev,
+                               "pull-up/down is unsupported for pin %u (%s)\n",
+                               pin->number, pin->name);
+                       return -EINVAL;
+               }
+
+               if (arg == 0)
+                       return 0; /* configuration ingored */
+               break;
+       default:
+               BUG();
+       }
+
+       pupdctrl = uniphier_pin_get_pupdctrl(pin->drv_data);
+
+       reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4;
+       shift = pupdctrl % 32;
+
+       return regmap_update_bits(priv->regmap, reg, 1 << shift, val << shift);
+}
+
+static int uniphier_conf_pin_drive_set(struct pinctrl_dev *pctldev,
+                                      const struct pinctrl_pin_desc *pin,
+                                      u16 strength)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       enum uniphier_pin_drv_str drv_str =
+                               uniphier_pin_get_drv_str(pin->drv_data);
+       const unsigned int strength_4_8[] = {4, 8, -1};
+       const unsigned int strength_8_12_16_20[] = {8, 12, 16, 20, -1};
+       const unsigned int *supported_strength;
+       unsigned int drvctrl, reg, shift, mask, width, val;
+
+       switch (drv_str) {
+       case UNIPHIER_PIN_DRV_4_8:
+               supported_strength = strength_4_8;
+               width = 1;
+               break;
+       case UNIPHIER_PIN_DRV_8_12_16_20:
+               supported_strength = strength_8_12_16_20;
+               width = 2;
+               break;
+       default:
+               dev_err(pctldev->dev,
+                       "cannot change drive strength for pin %u (%s)\n",
+                       pin->number, pin->name);
+               return -EINVAL;
+       }
+
+       for (val = 0; supported_strength[val] > 0; val++) {
+               if (supported_strength[val] > strength)
+                       break;
+       }
+
+       if (val == 0) {
+               dev_err(pctldev->dev,
+                       "unsupported drive strength %u mA for pin %u (%s)\n",
+                       strength, pin->number, pin->name);
+               return -EINVAL;
+       }
+
+       val--;
+
+       drvctrl = uniphier_pin_get_drvctrl(pin->drv_data);
+       drvctrl *= width;
+
+       reg = (width == 2) ? UNIPHIER_PINCTRL_DRV2CTRL_BASE :
+                            UNIPHIER_PINCTRL_DRVCTRL_BASE;
+
+       reg += drvctrl / 32 * 4;
+       shift = drvctrl % 32;
+       mask = (1U << width) - 1;
+
+       return regmap_update_bits(priv->regmap, reg,
+                                 mask << shift, val << shift);
+}
+
+static int uniphier_conf_pin_input_enable(struct pinctrl_dev *pctldev,
+                                         const struct pinctrl_pin_desc *pin,
+                                         u16 enable)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       unsigned int iectrl = uniphier_pin_get_iectrl(pin->drv_data);
+
+       if (enable == 0) {
+               /*
+                * Multiple pins share one input enable, so per-pin disabling
+                * is impossible.
+                */
+               dev_err(pctldev->dev, "unable to disable input\n");
+               return -EINVAL;
+       }
+
+       if (iectrl == UNIPHIER_PIN_IECTRL_NONE)
+               /* This pin is always input-enabled. nothing to do. */
+               return 0;
+
+       return regmap_update_bits(priv->regmap, UNIPHIER_PINCTRL_IECTRL,
+                                 BIT(iectrl), BIT(iectrl));
+}
+
+static int uniphier_conf_pin_config_set(struct pinctrl_dev *pctldev,
+                                       unsigned pin,
+                                       unsigned long *configs,
+                                       unsigned num_configs)
+{
+       const struct pinctrl_pin_desc *pin_desc = &pctldev->desc->pins[pin];
+       int i, ret;
+
+       for (i = 0; i < num_configs; i++) {
+               enum pin_config_param param =
+                                       pinconf_to_config_param(configs[i]);
+               u16 arg = pinconf_to_config_argument(configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_BIAS_DISABLE:
+               case PIN_CONFIG_BIAS_PULL_UP:
+               case PIN_CONFIG_BIAS_PULL_DOWN:
+               case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+                       ret = uniphier_conf_pin_bias_set(pctldev, pin_desc,
+                                                        param, arg);
+                       break;
+               case PIN_CONFIG_DRIVE_STRENGTH:
+                       ret = uniphier_conf_pin_drive_set(pctldev, pin_desc,
+                                                         arg);
+                       break;
+               case PIN_CONFIG_INPUT_ENABLE:
+                       ret = uniphier_conf_pin_input_enable(pctldev,
+                                                            pin_desc, arg);
+                       break;
+               default:
+                       dev_err(pctldev->dev,
+                               "unsupported configuration parameter %u\n",
+                               param);
+                       return -EINVAL;
+               }
+
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int uniphier_conf_pin_config_group_set(struct pinctrl_dev *pctldev,
+                                             unsigned selector,
+                                             unsigned long *configs,
+                                             unsigned num_configs)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       const unsigned *pins = priv->socdata->groups[selector].pins;
+       unsigned num_pins = priv->socdata->groups[selector].num_pins;
+       int i, ret;
+
+       for (i = 0; i < num_pins; i++) {
+               ret = uniphier_conf_pin_config_set(pctldev, pins[i],
+                                                  configs, num_configs);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static const struct pinconf_ops uniphier_confops = {
+       .is_generic = true,
+       .pin_config_get = uniphier_conf_pin_config_get,
+       .pin_config_set = uniphier_conf_pin_config_set,
+       .pin_config_group_set = uniphier_conf_pin_config_group_set,
+};
+
+static int uniphier_pmx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       return priv->socdata->functions_count;
+}
+
+static const char *uniphier_pmx_get_function_name(struct pinctrl_dev *pctldev,
+                                                 unsigned selector)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       return priv->socdata->functions[selector].name;
+}
+
+static int uniphier_pmx_get_function_groups(struct pinctrl_dev *pctldev,
+                                           unsigned selector,
+                                           const char * const **groups,
+                                           unsigned *num_groups)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+       *groups = priv->socdata->functions[selector].groups;
+       *num_groups = priv->socdata->functions[selector].num_groups;
+
+       return 0;
+}
+
+static int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin,
+                                   unsigned muxval)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       unsigned mux_bits = priv->socdata->mux_bits;
+       unsigned reg_stride = priv->socdata->reg_stride;
+       unsigned reg, reg_end, shift, mask;
+       int ret;
+
+       /* some pins need input-enabling */
+       ret = uniphier_conf_pin_input_enable(pctldev,
+                                            &pctldev->desc->pins[pin], 1);
+       if (ret)
+               return ret;
+
+       reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride;
+       reg_end = reg + reg_stride;
+       shift = pin * mux_bits % 32;
+       mask = (1U << mux_bits) - 1;
+
+       /*
+        * If reg_stride is greater than 4, the MSB of each pinsel shall be
+        * stored in the offset+4.
+        */
+       for (; reg < reg_end; reg += 4) {
+               ret = regmap_update_bits(priv->regmap, reg,
+                                        mask << shift, muxval << shift);
+               if (ret)
+                       return ret;
+               muxval >>= mux_bits;
+       }
+
+       if (priv->socdata->load_pinctrl) {
+               ret = regmap_write(priv->regmap,
+                                  UNIPHIER_PINCTRL_LOAD_PINMUX, 1);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int uniphier_pmx_set_mux(struct pinctrl_dev *pctldev,
+                               unsigned func_selector,
+                               unsigned group_selector)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       const struct uniphier_pinctrl_group *grp =
+                                       &priv->socdata->groups[group_selector];
+       int i;
+       int ret;
+
+       for (i = 0; i < grp->num_pins; i++) {
+               ret = uniphier_pmx_set_one_mux(pctldev, grp->pins[i],
+                                              grp->muxvals[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int uniphier_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
+                                           struct pinctrl_gpio_range *range,
+                                           unsigned offset)
+{
+       struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+       const struct uniphier_pinctrl_group *groups = priv->socdata->groups;
+       int groups_count = priv->socdata->groups_count;
+       enum uniphier_pinmux_gpio_range_type range_type;
+       int i, j;
+
+       if (strstr(range->name, "irq"))
+               range_type = UNIPHIER_PINMUX_GPIO_RANGE_IRQ;
+       else
+               range_type = UNIPHIER_PINMUX_GPIO_RANGE_PORT;
+
+       for (i = 0; i < groups_count; i++) {
+               if (groups[i].range_type != range_type)
+                       continue;
+
+               for (j = 0; j < groups[i].num_pins; j++)
+                       if (groups[i].pins[j] == offset)
+                               goto found;
+       }
+
+       dev_err(pctldev->dev, "pin %u does not support GPIO\n", offset);
+       return -EINVAL;
+
+found:
+       return uniphier_pmx_set_one_mux(pctldev, offset, groups[i].muxvals[j]);
+}
+
+static const struct pinmux_ops uniphier_pmxops = {
+       .get_functions_count = uniphier_pmx_get_functions_count,
+       .get_function_name = uniphier_pmx_get_function_name,
+       .get_function_groups = uniphier_pmx_get_function_groups,
+       .set_mux = uniphier_pmx_set_mux,
+       .gpio_request_enable = uniphier_pmx_gpio_request_enable,
+       .strict = true,
+};
+
+int uniphier_pinctrl_probe(struct platform_device *pdev,
+                          struct pinctrl_desc *desc,
+                          struct uniphier_pinctrl_socdata *socdata)
+{
+       struct device *dev = &pdev->dev;
+       struct uniphier_pinctrl_priv *priv;
+
+       if (!socdata ||
+           !socdata->groups ||
+           !socdata->groups_count ||
+           !socdata->functions ||
+           !socdata->functions_count ||
+           !socdata->mux_bits ||
+           !socdata->reg_stride) {
+               dev_err(dev, "pinctrl socdata lacks necessary members\n");
+               return -EINVAL;
+       }
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->regmap = syscon_node_to_regmap(dev->of_node);
+       if (IS_ERR(priv->regmap)) {
+               dev_err(dev, "failed to get regmap\n");
+               return PTR_ERR(priv->regmap);
+       }
+
+       priv->socdata = socdata;
+       desc->pctlops = &uniphier_pctlops;
+       desc->pmxops = &uniphier_pmxops;
+       desc->confops = &uniphier_confops;
+
+       priv->pctldev = pinctrl_register(desc, dev, priv);
+       if (IS_ERR(priv->pctldev)) {
+               dev_err(dev, "failed to register UniPhier pinctrl driver\n");
+               return PTR_ERR(priv->pctldev);
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(uniphier_pinctrl_probe);
+
+int uniphier_pinctrl_remove(struct platform_device *pdev)
+{
+       struct uniphier_pinctrl_priv *priv = platform_get_drvdata(pdev);
+
+       pinctrl_unregister(priv->pctldev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(uniphier_pinctrl_remove);