Merge git://git.denx.de/u-boot-fsl-qoriq
[oweals/u-boot.git] / drivers / pinctrl / mvebu / pinctrl-armada-37xx.c
index 6a31d98937d3ac635a1138062f33de03bbd58342..2bf853eba13d237d3c6882daae08128fdb18e815 100644 (file)
 #include <common.h>
 #include <config.h>
 #include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
 #include <dm/pinctrl.h>
 #include <dm/root.h>
 #include <errno.h>
 #include <fdtdec.h>
 #include <regmap.h>
+#include <asm/gpio.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 #define OUTPUT_EN      0x0
+#define INPUT_VAL      0x10
+#define OUTPUT_VAL     0x18
 #define OUTPUT_CTL     0x20
 #define SELECTION      0x30
 
@@ -154,8 +159,9 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = {
        PIN_GRP_GPIO("onewire", 4, 1, BIT(16), "onewire"),
        PIN_GRP_GPIO("uart1", 25, 2, BIT(17), "uart"),
        PIN_GRP_GPIO("spi_quad", 15, 2, BIT(18), "spi"),
-       PIN_GRP_EXTRA("uart2", 9, 2, BIT(13) | BIT(14) | BIT(19),
-                     BIT(13) | BIT(14), BIT(19), 18, 2, "gpio", "uart"),
+       PIN_GRP_EXTRA("uart2", 9, 2, BIT(1) | BIT(13) | BIT(14) | BIT(19),
+                     BIT(1) | BIT(13) | BIT(14), BIT(1) | BIT(19),
+                     18, 2, "gpio", "uart"),
        PIN_GRP_GPIO("led0_od", 11, 1, BIT(20), "led"),
        PIN_GRP_GPIO("led1_od", 12, 1, BIT(21), "led"),
        PIN_GRP_GPIO("led2_od", 13, 1, BIT(22), "led"),
@@ -189,6 +195,16 @@ const struct armada_37xx_pin_data armada_37xx_pin_sb = {
        .ngroups = ARRAY_SIZE(armada_37xx_sb_groups),
 };
 
+static inline void armada_37xx_update_reg(unsigned int *reg,
+                                         unsigned int offset)
+{
+       /* We never have more than 2 registers */
+       if (offset >= GPIO_PER_REG) {
+               offset -= GPIO_PER_REG;
+               *reg += sizeof(u32);
+       }
+}
+
 static int armada_37xx_get_func_reg(struct armada_37xx_pin_group *grp,
                                    const char *func)
 {
@@ -399,6 +415,149 @@ static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info)
        return 0;
 }
 
+static int armada_37xx_gpio_get(struct udevice *dev, unsigned int offset)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       unsigned int reg = INPUT_VAL;
+       unsigned int val, mask;
+
+       armada_37xx_update_reg(&reg, offset);
+       mask = BIT(offset);
+
+       val = readl(info->base + reg);
+
+       return (val & mask) != 0;
+}
+
+static int armada_37xx_gpio_set(struct udevice *dev, unsigned int offset,
+                               int value)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       unsigned int reg = OUTPUT_VAL;
+       unsigned int mask, val;
+
+       armada_37xx_update_reg(&reg, offset);
+       mask = BIT(offset);
+       val = value ? mask : 0;
+
+       clrsetbits_le32(info->base + reg, mask, val);
+
+       return 0;
+}
+
+static int armada_37xx_gpio_get_direction(struct udevice *dev,
+                                         unsigned int offset)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       unsigned int reg = OUTPUT_EN;
+       unsigned int val, mask;
+
+       armada_37xx_update_reg(&reg, offset);
+       mask = BIT(offset);
+       val = readl(info->base + reg);
+
+       if (val & mask)
+               return GPIOF_OUTPUT;
+       else
+               return GPIOF_INPUT;
+}
+
+static int armada_37xx_gpio_direction_input(struct udevice *dev,
+                                           unsigned int offset)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       unsigned int reg = OUTPUT_EN;
+       unsigned int mask;
+
+       armada_37xx_update_reg(&reg, offset);
+       mask = BIT(offset);
+
+       clrbits_le32(info->base + reg, mask);
+
+       return 0;
+}
+
+static int armada_37xx_gpio_direction_output(struct udevice *dev,
+                                            unsigned int offset, int value)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       unsigned int reg = OUTPUT_EN;
+       unsigned int mask;
+
+       armada_37xx_update_reg(&reg, offset);
+       mask = BIT(offset);
+
+       setbits_le32(info->base + reg, mask);
+
+       /* And set the requested value */
+       return armada_37xx_gpio_set(dev, offset, value);
+}
+
+static int armada_37xx_gpio_probe(struct udevice *dev)
+{
+       struct armada_37xx_pinctrl *info = dev_get_priv(dev->parent);
+       struct gpio_dev_priv *uc_priv;
+
+       uc_priv = dev_get_uclass_priv(dev);
+       uc_priv->bank_name = info->data->name;
+       uc_priv->gpio_count = info->data->nr_pins;
+
+       return 0;
+}
+
+static const struct dm_gpio_ops armada_37xx_gpio_ops = {
+       .set_value = armada_37xx_gpio_set,
+       .get_value = armada_37xx_gpio_get,
+       .get_function = armada_37xx_gpio_get_direction,
+       .direction_input = armada_37xx_gpio_direction_input,
+       .direction_output = armada_37xx_gpio_direction_output,
+};
+
+static struct driver armada_37xx_gpio_driver = {
+       .name   = "armada-37xx-gpio",
+       .id     = UCLASS_GPIO,
+       .probe  = armada_37xx_gpio_probe,
+       .ops    = &armada_37xx_gpio_ops,
+};
+
+static int armada_37xx_gpiochip_register(struct udevice *parent,
+                                        struct armada_37xx_pinctrl *info)
+{
+       const void *blob = gd->fdt_blob;
+       int node = dev_of_offset(parent);
+       struct uclass_driver *drv;
+       struct udevice *dev;
+       int ret = -ENODEV;
+       int subnode;
+       char *name;
+
+       /* Lookup GPIO driver */
+       drv = lists_uclass_lookup(UCLASS_GPIO);
+       if (!drv) {
+               puts("Cannot find GPIO driver\n");
+               return -ENOENT;
+       }
+
+       fdt_for_each_subnode(subnode, blob, node) {
+               if (fdtdec_get_bool(blob, subnode, "gpio-controller")) {
+                       ret = 0;
+                       break;
+               }
+       };
+       if (ret)
+               return ret;
+
+       name = calloc(1, 32);
+       sprintf(name, "armada-37xx-gpio");
+
+       /* Create child device UCLASS_GPIO and bind it */
+       device_bind(parent, &armada_37xx_gpio_driver, name, NULL, subnode,
+                   &dev);
+       dev_set_of_offset(dev, subnode);
+
+       return 0;
+}
+
 const struct pinctrl_ops armada_37xx_pinctrl_ops  = {
        .get_groups_count = armada_37xx_pmx_get_groups_count,
        .get_group_name = armada_37xx_pmx_get_group_name,
@@ -417,9 +576,9 @@ int armada_37xx_pinctrl_probe(struct udevice *dev)
        info->data = (struct armada_37xx_pin_data *)dev_get_driver_data(dev);
        pin_data = info->data;
 
-       info->base = (void __iomem *)dev_get_addr(dev);
+       info->base = (void __iomem *)devfdt_get_addr(dev);
        if (!info->base) {
-               error("unable to find regmap\n");
+               pr_err("unable to find regmap\n");
                return -ENODEV;
        }
 
@@ -444,6 +603,10 @@ int armada_37xx_pinctrl_probe(struct udevice *dev)
        if (ret)
                return ret;
 
+       ret = armada_37xx_gpiochip_register(dev, info);
+       if (ret)
+               return ret;
+
        return 0;
 }