pinctrl: rp1: Implement RaspberryPi RP1 gpio support
authorAndrea della Porta <andrea.porta@suse.com>
Thu, 29 May 2025 13:50:42 +0000 (15:50 +0200)
committerFlorian Fainelli <florian.fainelli@broadcom.com>
Mon, 16 Jun 2025 18:50:34 +0000 (11:50 -0700)
The RP1 is an MFD supporting a gpio controller and /pinmux/pinctrl.
Add minimum support for the gpio only portion. The driver is in
pinctrl folder since upcoming patches will add the pinmux/pinctrl
support where the gpio part can be seen as an addition.

Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Stefan Wahren <wahrenst@gmx.net>
Link: https://lore.kernel.org/r/20250529135052.28398-5-andrea.porta@suse.com
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/pinctrl-rp1.c [new file with mode: 0644]

index 33db9104df178e5a3148b60c3c6bd153113385d3..eb1b37af81fb81175d22018cf4856bfafaea75db 100644 (file)
@@ -624,6 +624,17 @@ config PINCTRL_MLXBF3
          each pin. This driver can also be built as a module called
          pinctrl-mlxbf3.
 
+config PINCTRL_RP1
+       tristate "Pinctrl driver for RP1"
+       depends on MISC_RP1
+       default MISC_RP1
+       select PINMUX
+       select PINCONF
+       select GENERIC_PINCONF
+       help
+         Enable the gpio and pinctrl/mux driver for RaspberryPi RP1
+         multi function device.
+
 source "drivers/pinctrl/actions/Kconfig"
 source "drivers/pinctrl/aspeed/Kconfig"
 source "drivers/pinctrl/bcm/Kconfig"
index ac27e88677d14f1c697e0d0be9f295c746556f4d..65dac8e387985ac7f865d13325eb903f64645a2a 100644 (file)
@@ -49,6 +49,7 @@ obj-$(CONFIG_PINCTRL_PIC32)   += pinctrl-pic32.o
 obj-$(CONFIG_PINCTRL_PISTACHIO)        += pinctrl-pistachio.o
 obj-$(CONFIG_PINCTRL_RK805)    += pinctrl-rk805.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
+obj-$(CONFIG_PINCTRL_RP1)       += pinctrl-rp1.o
 obj-$(CONFIG_PINCTRL_SCMI)     += pinctrl-scmi.o
 obj-$(CONFIG_PINCTRL_SINGLE)   += pinctrl-single.o
 obj-$(CONFIG_PINCTRL_ST)       += pinctrl-st.o
diff --git a/drivers/pinctrl/pinctrl-rp1.c b/drivers/pinctrl/pinctrl-rp1.c
new file mode 100644 (file)
index 0000000..07d54bb
--- /dev/null
@@ -0,0 +1,790 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Raspberry Pi RP1 GPIO unit
+ *
+ * Copyright (C) 2023 Raspberry Pi Ltd.
+ *
+ * This driver is inspired by:
+ * pinctrl-bcm2835.c, please see original file for copyright information
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MODULE_NAME "pinctrl-rp1"
+#define RP1_NUM_GPIOS  54
+#define RP1_NUM_BANKS  3
+
+#define RP1_INT_EDGE_FALLING           BIT(0)
+#define RP1_INT_EDGE_RISING            BIT(1)
+#define RP1_INT_LEVEL_LOW              BIT(2)
+#define RP1_INT_LEVEL_HIGH             BIT(3)
+#define RP1_INT_MASK                   GENMASK(3, 0)
+#define RP1_INT_EDGE_BOTH              (RP1_INT_EDGE_FALLING | \
+                                        RP1_INT_EDGE_RISING)
+
+#define RP1_FSEL_COUNT                 9
+
+#define RP1_FSEL_ALT0                  0x00
+#define RP1_FSEL_GPIO                  0x05
+#define RP1_FSEL_NONE                  0x09
+#define RP1_FSEL_NONE_HW               0x1f
+
+#define RP1_PAD_DRIVE_2MA              0x0
+#define RP1_PAD_DRIVE_4MA              0x1
+#define RP1_PAD_DRIVE_8MA              0x2
+#define RP1_PAD_DRIVE_12MA             0x3
+
+enum {
+       RP1_PUD_OFF                     = 0,
+       RP1_PUD_DOWN                    = 1,
+       RP1_PUD_UP                      = 2,
+};
+
+enum {
+       RP1_DIR_OUTPUT                  = 0,
+       RP1_DIR_INPUT                   = 1,
+};
+
+enum {
+       RP1_OUTOVER_PERI                = 0,
+       RP1_OUTOVER_INVPERI             = 1,
+       RP1_OUTOVER_LOW                 = 2,
+       RP1_OUTOVER_HIGH                = 3,
+};
+
+enum {
+       RP1_OEOVER_PERI                 = 0,
+       RP1_OEOVER_INVPERI              = 1,
+       RP1_OEOVER_DISABLE              = 2,
+       RP1_OEOVER_ENABLE               = 3,
+};
+
+enum {
+       RP1_INOVER_PERI                 = 0,
+       RP1_INOVER_INVPERI              = 1,
+       RP1_INOVER_LOW                  = 2,
+       RP1_INOVER_HIGH                 = 3,
+};
+
+enum {
+       RP1_GPIO_CTRL_IRQRESET_SET              = 0,
+       RP1_GPIO_CTRL_INT_CLR                   = 1,
+       RP1_GPIO_CTRL_INT_SET                   = 2,
+       RP1_GPIO_CTRL_OEOVER                    = 3,
+       RP1_GPIO_CTRL_FUNCSEL                   = 4,
+       RP1_GPIO_CTRL_OUTOVER                   = 5,
+       RP1_GPIO_CTRL                           = 6,
+};
+
+enum {
+       RP1_INTE_SET                    = 0,
+       RP1_INTE_CLR                    = 1,
+};
+
+enum {
+       RP1_RIO_OUT_SET                 = 0,
+       RP1_RIO_OUT_CLR                 = 1,
+       RP1_RIO_OE                      = 2,
+       RP1_RIO_OE_SET                  = 3,
+       RP1_RIO_OE_CLR                  = 4,
+       RP1_RIO_IN                      = 5,
+};
+
+enum {
+       RP1_PAD_SLEWFAST                = 0,
+       RP1_PAD_SCHMITT                 = 1,
+       RP1_PAD_PULL                    = 2,
+       RP1_PAD_DRIVE                   = 3,
+       RP1_PAD_IN_ENABLE               = 4,
+       RP1_PAD_OUT_DISABLE             = 5,
+};
+
+static const struct reg_field rp1_gpio_fields[] = {
+       [RP1_GPIO_CTRL_IRQRESET_SET]    = REG_FIELD(0x2004, 28, 28),
+       [RP1_GPIO_CTRL_INT_CLR]         = REG_FIELD(0x3004, 20, 23),
+       [RP1_GPIO_CTRL_INT_SET]         = REG_FIELD(0x2004, 20, 23),
+       [RP1_GPIO_CTRL_OEOVER]          = REG_FIELD(0x0004, 14, 15),
+       [RP1_GPIO_CTRL_FUNCSEL]         = REG_FIELD(0x0004, 0, 4),
+       [RP1_GPIO_CTRL_OUTOVER]         = REG_FIELD(0x0004, 12, 13),
+       [RP1_GPIO_CTRL]                 = REG_FIELD(0x0004, 0, 31),
+};
+
+static const struct reg_field rp1_inte_fields[] = {
+       [RP1_INTE_SET]                  = REG_FIELD(0x2000, 0, 0),
+       [RP1_INTE_CLR]                  = REG_FIELD(0x3000, 0, 0),
+};
+
+static const struct reg_field rp1_rio_fields[] = {
+       [RP1_RIO_OUT_SET]               = REG_FIELD(0x2000, 0, 0),
+       [RP1_RIO_OUT_CLR]               = REG_FIELD(0x3000, 0, 0),
+       [RP1_RIO_OE]                    = REG_FIELD(0x0004, 0, 0),
+       [RP1_RIO_OE_SET]                = REG_FIELD(0x2004, 0, 0),
+       [RP1_RIO_OE_CLR]                = REG_FIELD(0x3004, 0, 0),
+       [RP1_RIO_IN]                    = REG_FIELD(0x0008, 0, 0),
+};
+
+static const struct reg_field rp1_pad_fields[] = {
+       [RP1_PAD_SLEWFAST]              = REG_FIELD(0, 0, 0),
+       [RP1_PAD_SCHMITT]               = REG_FIELD(0, 1, 1),
+       [RP1_PAD_PULL]                  = REG_FIELD(0, 2, 3),
+       [RP1_PAD_DRIVE]                 = REG_FIELD(0, 4, 5),
+       [RP1_PAD_IN_ENABLE]             = REG_FIELD(0, 6, 6),
+       [RP1_PAD_OUT_DISABLE]           = REG_FIELD(0, 7, 7),
+};
+
+struct rp1_iobank_desc {
+       int min_gpio;
+       int num_gpios;
+       int gpio_offset;
+       int inte_offset;
+       int ints_offset;
+       int rio_offset;
+       int pads_offset;
+};
+
+struct rp1_pin_info {
+       u8 num;
+       u8 bank;
+       u8 offset;
+       u8 fsel;
+       u8 irq_type;
+
+       struct regmap_field *gpio[ARRAY_SIZE(rp1_gpio_fields)];
+       struct regmap_field *rio[ARRAY_SIZE(rp1_rio_fields)];
+       struct regmap_field *inte[ARRAY_SIZE(rp1_inte_fields)];
+       struct regmap_field *pad[ARRAY_SIZE(rp1_pad_fields)];
+};
+
+struct rp1_pinctrl {
+       struct device *dev;
+       void __iomem *gpio_base;
+       void __iomem *rio_base;
+       void __iomem *pads_base;
+       int irq[RP1_NUM_BANKS];
+       struct rp1_pin_info pins[RP1_NUM_GPIOS];
+
+       struct pinctrl_dev *pctl_dev;
+       struct gpio_chip gpio_chip;
+       struct pinctrl_gpio_range gpio_range;
+
+       raw_spinlock_t irq_lock[RP1_NUM_BANKS];
+};
+
+static const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = {
+       /*         gpio   inte    ints     rio    pads */
+       {  0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 },
+       { 28,  6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 },
+       { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 },
+};
+
+static int rp1_pinconf_set(struct rp1_pin_info *pin,
+                          unsigned int offset, unsigned long *configs,
+                          unsigned int num_configs);
+
+static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip,
+                                       unsigned int offset)
+{
+       struct rp1_pinctrl *pc = gpiochip_get_data(chip);
+
+       if (pc && offset < RP1_NUM_GPIOS)
+               return &pc->pins[offset];
+       return NULL;
+}
+
+static void rp1_input_enable(struct rp1_pin_info *pin, int value)
+{
+       regmap_field_write(pin->pad[RP1_PAD_IN_ENABLE], !!value);
+}
+
+static void rp1_output_enable(struct rp1_pin_info *pin, int value)
+{
+       regmap_field_write(pin->pad[RP1_PAD_OUT_DISABLE], !value);
+}
+
+static u32 rp1_get_fsel(struct rp1_pin_info *pin)
+{
+       u32 oeover, fsel;
+
+       regmap_field_read(pin->gpio[RP1_GPIO_CTRL_OEOVER], &oeover);
+       regmap_field_read(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], &fsel);
+
+       if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT)
+               fsel = RP1_FSEL_NONE;
+
+       return fsel;
+}
+
+static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel)
+{
+       if (fsel >= RP1_FSEL_COUNT)
+               fsel = RP1_FSEL_NONE_HW;
+
+       rp1_input_enable(pin, 1);
+       rp1_output_enable(pin, 1);
+
+       if (fsel == RP1_FSEL_NONE) {
+               regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_DISABLE);
+       } else {
+               regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OUTOVER], RP1_OUTOVER_PERI);
+               regmap_field_write(pin->gpio[RP1_GPIO_CTRL_OEOVER], RP1_OEOVER_PERI);
+       }
+
+       regmap_field_write(pin->gpio[RP1_GPIO_CTRL_FUNCSEL], fsel);
+}
+
+static int rp1_get_dir(struct rp1_pin_info *pin)
+{
+       unsigned int val;
+
+       regmap_field_read(pin->rio[RP1_RIO_OE], &val);
+
+       return !val ? RP1_DIR_INPUT : RP1_DIR_OUTPUT;
+}
+
+static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
+{
+       int reg = is_input ? RP1_RIO_OE_CLR : RP1_RIO_OE_SET;
+
+       regmap_field_write(pin->rio[reg], 1);
+}
+
+static int rp1_get_value(struct rp1_pin_info *pin)
+{
+       unsigned int val;
+
+       regmap_field_read(pin->rio[RP1_RIO_IN], &val);
+
+       return !!val;
+}
+
+static void rp1_set_value(struct rp1_pin_info *pin, int value)
+{
+       /* Assume the pin is already an output */
+       int reg = value ? RP1_RIO_OUT_SET : RP1_RIO_OUT_CLR;
+
+       regmap_field_write(pin->rio[reg], 1);
+}
+
+static int rp1_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+       int ret;
+
+       if (!pin)
+               return -EINVAL;
+
+       ret = rp1_get_value(pin);
+
+       return ret;
+}
+
+static void rp1_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+
+       if (pin)
+               rp1_set_value(pin, value);
+}
+
+static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+       u32 fsel;
+
+       if (!pin)
+               return -EINVAL;
+
+       fsel = rp1_get_fsel(pin);
+       if (fsel != RP1_FSEL_GPIO)
+               return -EINVAL;
+
+       return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ?
+               GPIO_LINE_DIRECTION_OUT :
+               GPIO_LINE_DIRECTION_IN;
+}
+
+static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+
+       if (!pin)
+               return -EINVAL;
+       rp1_set_dir(pin, RP1_DIR_INPUT);
+       rp1_set_fsel(pin, RP1_FSEL_GPIO);
+
+       return 0;
+}
+
+static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
+                                    int value)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+
+       if (!pin)
+               return -EINVAL;
+       rp1_set_value(pin, value);
+       rp1_set_dir(pin, RP1_DIR_OUTPUT);
+       rp1_set_fsel(pin, RP1_FSEL_GPIO);
+
+       return 0;
+}
+
+static int rp1_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+                              unsigned long config)
+{
+       struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
+       unsigned long configs[] = { config };
+
+       return rp1_pinconf_set(pin, offset, configs,
+                              ARRAY_SIZE(configs));
+}
+
+static const struct gpio_chip rp1_gpio_chip = {
+       .label = MODULE_NAME,
+       .owner = THIS_MODULE,
+       .request = gpiochip_generic_request,
+       .free = gpiochip_generic_free,
+       .direction_input = rp1_gpio_direction_input,
+       .direction_output = rp1_gpio_direction_output,
+       .get_direction = rp1_gpio_get_direction,
+       .get = rp1_gpio_get,
+       .set = rp1_gpio_set,
+       .base = -1,
+       .set_config = rp1_gpio_set_config,
+       .ngpio = RP1_NUM_GPIOS,
+       .can_sleep = false,
+};
+
+static void rp1_gpio_irq_handler(struct irq_desc *desc)
+{
+       struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+       struct irq_chip *host_chip = irq_desc_get_chip(desc);
+       struct rp1_pinctrl *pc = gpiochip_get_data(chip);
+       const struct rp1_iobank_desc *bank;
+       int irq = irq_desc_get_irq(desc);
+       unsigned long ints;
+       int bit_pos;
+
+       if (pc->irq[0] == irq)
+               bank = &rp1_iobanks[0];
+       else if (pc->irq[1] == irq)
+               bank = &rp1_iobanks[1];
+       else
+               bank = &rp1_iobanks[2];
+
+       chained_irq_enter(host_chip, desc);
+
+       ints = readl(pc->gpio_base + bank->ints_offset);
+       for_each_set_bit(bit_pos, &ints, 32) {
+               struct rp1_pin_info *pin = rp1_get_pin(chip, bit_pos);
+
+               regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
+               generic_handle_irq(irq_find_mapping(pc->gpio_chip.irq.domain,
+                                                   bank->gpio_offset + bit_pos));
+       }
+
+       chained_irq_exit(host_chip, desc);
+}
+
+static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable)
+{
+       int reg = enable ? RP1_INTE_SET : RP1_INTE_CLR;
+
+       regmap_field_write(pin->inte[reg], 1);
+       if (!enable)
+               /* Clear any latched events */
+               regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
+}
+
+static void rp1_gpio_irq_enable(struct irq_data *data)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+       unsigned int gpio = irqd_to_hwirq(data);
+       struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
+
+       rp1_gpio_irq_config(pin, true);
+}
+
+static void rp1_gpio_irq_disable(struct irq_data *data)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+       unsigned int gpio = irqd_to_hwirq(data);
+       struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
+
+       rp1_gpio_irq_config(pin, false);
+}
+
+static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type)
+{
+       u32 irq_flags;
+
+       switch (type) {
+       case IRQ_TYPE_NONE:
+               irq_flags = 0;
+               break;
+       case IRQ_TYPE_EDGE_RISING:
+               irq_flags = RP1_INT_EDGE_RISING;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               irq_flags = RP1_INT_EDGE_FALLING;
+               break;
+       case IRQ_TYPE_EDGE_BOTH:
+               irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               irq_flags = RP1_INT_LEVEL_HIGH;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               irq_flags = RP1_INT_LEVEL_LOW;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       /* Clear them all */
+       regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_CLR], RP1_INT_MASK);
+
+       /* Set those that are needed */
+       regmap_field_write(pin->gpio[RP1_GPIO_CTRL_INT_SET], irq_flags);
+       pin->irq_type = type;
+
+       return 0;
+}
+
+static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+       unsigned int gpio = irqd_to_hwirq(data);
+       struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
+       struct rp1_pinctrl *pc = gpiochip_get_data(chip);
+       int bank = pin->bank;
+       unsigned long flags;
+       int ret;
+
+       raw_spin_lock_irqsave(&pc->irq_lock[bank], flags);
+
+       ret = rp1_irq_set_type(pin, type);
+       if (!ret) {
+               if (type & IRQ_TYPE_EDGE_BOTH)
+                       irq_set_handler_locked(data, handle_edge_irq);
+               else
+                       irq_set_handler_locked(data, handle_level_irq);
+       }
+
+       raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags);
+
+       return ret;
+}
+
+static void rp1_gpio_irq_ack(struct irq_data *data)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+       unsigned int gpio = irqd_to_hwirq(data);
+       struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
+
+       /* Clear any latched events */
+       regmap_field_write(pin->gpio[RP1_GPIO_CTRL_IRQRESET_SET], 1);
+}
+
+static struct irq_chip rp1_gpio_irq_chip = {
+       .name = MODULE_NAME,
+       .irq_enable = rp1_gpio_irq_enable,
+       .irq_disable = rp1_gpio_irq_disable,
+       .irq_set_type = rp1_gpio_irq_set_type,
+       .irq_ack = rp1_gpio_irq_ack,
+       .irq_mask = rp1_gpio_irq_disable,
+       .irq_unmask = rp1_gpio_irq_enable,
+       .flags = IRQCHIP_IMMUTABLE,
+       GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg)
+{
+       regmap_field_write(pin->pad[RP1_PAD_PULL], arg & 0x3);
+}
+
+static int rp1_pinconf_set(struct rp1_pin_info *pin, unsigned int offset,
+                          unsigned long *configs, unsigned int num_configs)
+{
+       u32 param, arg;
+       int i;
+
+       if (!pin)
+               return -EINVAL;
+
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               arg = pinconf_to_config_argument(configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_BIAS_DISABLE:
+                       rp1_pull_config_set(pin, RP1_PUD_OFF);
+                       break;
+
+               case PIN_CONFIG_BIAS_PULL_DOWN:
+                       rp1_pull_config_set(pin, RP1_PUD_DOWN);
+                       break;
+
+               case PIN_CONFIG_BIAS_PULL_UP:
+                       rp1_pull_config_set(pin, RP1_PUD_UP);
+                       break;
+
+               case PIN_CONFIG_INPUT_ENABLE:
+                       rp1_input_enable(pin, arg);
+                       break;
+
+               case PIN_CONFIG_OUTPUT_ENABLE:
+                       rp1_output_enable(pin, arg);
+                       break;
+
+               case PIN_CONFIG_OUTPUT:
+                       rp1_set_value(pin, arg);
+                       rp1_set_dir(pin, RP1_DIR_OUTPUT);
+                       rp1_set_fsel(pin, RP1_FSEL_GPIO);
+                       break;
+
+               case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+                       regmap_field_write(pin->pad[RP1_PAD_SCHMITT], !!arg);
+                       break;
+
+               case PIN_CONFIG_SLEW_RATE:
+                       regmap_field_write(pin->pad[RP1_PAD_SLEWFAST], !!arg);
+                       break;
+
+               case PIN_CONFIG_DRIVE_STRENGTH:
+                       switch (arg) {
+                       case 2:
+                               arg = RP1_PAD_DRIVE_2MA;
+                               break;
+                       case 4:
+                               arg = RP1_PAD_DRIVE_4MA;
+                               break;
+                       case 8:
+                               arg = RP1_PAD_DRIVE_8MA;
+                               break;
+                       case 12:
+                               arg = RP1_PAD_DRIVE_12MA;
+                               break;
+                       default:
+                               return -ENOTSUPP;
+                       }
+                       regmap_field_write(pin->pad[RP1_PAD_DRIVE], arg);
+                       break;
+
+               default:
+                       return -ENOTSUPP;
+
+               } /* switch param type */
+       } /* for each config */
+
+       return 0;
+}
+
+static const struct of_device_id rp1_pinctrl_match[] = {
+       { .compatible = "raspberrypi,rp1-gpio" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rp1_pinctrl_match);
+
+static struct rp1_pinctrl rp1_pinctrl_data = {};
+
+static const struct regmap_config rp1_pinctrl_regmap_cfg = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .fast_io = true,
+       .name = "rp1-pinctrl",
+};
+
+static int rp1_gen_regfield(struct device *dev,
+                           const struct reg_field *array,
+                           size_t array_size,
+                           int reg_off,
+                           int pin_off,
+                           bool additive_offset,
+                           struct regmap *regmap,
+                           struct regmap_field *out[])
+{
+       struct reg_field regfield;
+       int k;
+
+       for (k = 0; k < array_size; k++) {
+               regfield = array[k];
+               regfield.reg = (additive_offset ? regfield.reg : 0) + reg_off;
+               if (pin_off >= 0) {
+                       regfield.lsb = pin_off;
+                       regfield.msb = regfield.lsb;
+               }
+               out[k] = devm_regmap_field_alloc(dev, regmap, regfield);
+
+               if (IS_ERR(out[k]))
+                       return PTR_ERR(out[k]);
+       }
+
+       return 0;
+}
+
+static int rp1_pinctrl_probe(struct platform_device *pdev)
+{
+       struct regmap *gpio_regmap, *rio_regmap, *pads_regmap;
+       struct rp1_pinctrl *pc = &rp1_pinctrl_data;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct gpio_irq_chip *girq;
+       int err, i;
+
+       pc->dev = dev;
+       pc->gpio_chip = rp1_gpio_chip;
+       pc->gpio_chip.parent = dev;
+
+       pc->gpio_base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(pc->gpio_base))
+               return dev_err_probe(dev, PTR_ERR(pc->gpio_base), "could not get GPIO IO memory\n");
+
+       pc->rio_base = devm_platform_ioremap_resource(pdev, 1);
+       if (IS_ERR(pc->rio_base))
+               return dev_err_probe(dev, PTR_ERR(pc->rio_base), "could not get RIO IO memory\n");
+
+       pc->pads_base = devm_platform_ioremap_resource(pdev, 2);
+       if (IS_ERR(pc->pads_base))
+               return dev_err_probe(dev, PTR_ERR(pc->pads_base), "could not get PADS IO memory\n");
+
+       gpio_regmap = devm_regmap_init_mmio(dev, pc->gpio_base,
+                                           &rp1_pinctrl_regmap_cfg);
+       if (IS_ERR(gpio_regmap))
+               return dev_err_probe(dev, PTR_ERR(gpio_regmap), "could not init GPIO regmap\n");
+
+       rio_regmap = devm_regmap_init_mmio(dev, pc->rio_base,
+                                          &rp1_pinctrl_regmap_cfg);
+       if (IS_ERR(rio_regmap))
+               return dev_err_probe(dev, PTR_ERR(rio_regmap), "could not init RIO regmap\n");
+
+       pads_regmap = devm_regmap_init_mmio(dev, pc->pads_base,
+                                           &rp1_pinctrl_regmap_cfg);
+       if (IS_ERR(pads_regmap))
+               return dev_err_probe(dev, PTR_ERR(pads_regmap), "could not init PADS regmap\n");
+
+       for (i = 0; i < RP1_NUM_BANKS; i++) {
+               const struct rp1_iobank_desc *bank = &rp1_iobanks[i];
+               int j;
+
+               for (j = 0; j < bank->num_gpios; j++) {
+                       struct rp1_pin_info *pin =
+                               &pc->pins[bank->min_gpio + j];
+                       int reg_off;
+
+                       pin->num = bank->min_gpio + j;
+                       pin->bank = i;
+                       pin->offset = j;
+
+                       reg_off = bank->gpio_offset + pin->offset *
+                                 sizeof(u32) * 2;
+                       err = rp1_gen_regfield(dev,
+                                              rp1_gpio_fields,
+                                              ARRAY_SIZE(rp1_gpio_fields),
+                                              reg_off,
+                                              -1,
+                                              true,
+                                              gpio_regmap,
+                                              pin->gpio);
+
+                       if (err)
+                               return dev_err_probe(dev, err,
+                                                    "Unable to allocate regmap for gpio\n");
+
+                       reg_off = bank->inte_offset;
+                       err = rp1_gen_regfield(dev,
+                                              rp1_inte_fields,
+                                              ARRAY_SIZE(rp1_inte_fields),
+                                              reg_off,
+                                              pin->offset,
+                                              true,
+                                              gpio_regmap,
+                                              pin->inte);
+
+                       if (err)
+                               return dev_err_probe(dev, err,
+                                                    "Unable to allocate regmap for inte\n");
+
+                       reg_off = bank->rio_offset;
+                       err = rp1_gen_regfield(dev,
+                                              rp1_rio_fields,
+                                              ARRAY_SIZE(rp1_rio_fields),
+                                              reg_off,
+                                              pin->offset,
+                                              true,
+                                              rio_regmap,
+                                              pin->rio);
+
+                       if (err)
+                               return dev_err_probe(dev, err,
+                                                    "Unable to allocate regmap for rio\n");
+
+                       reg_off = bank->pads_offset + pin->offset * sizeof(u32);
+                       err = rp1_gen_regfield(dev,
+                                              rp1_pad_fields,
+                                              ARRAY_SIZE(rp1_pad_fields),
+                                              reg_off,
+                                              -1,
+                                              false,
+                                              pads_regmap,
+                                              pin->pad);
+
+                       if (err)
+                               return dev_err_probe(dev, err,
+                                                    "Unable to allocate regmap for pad\n");
+               }
+
+               raw_spin_lock_init(&pc->irq_lock[i]);
+       }
+
+       girq = &pc->gpio_chip.irq;
+       girq->chip = &rp1_gpio_irq_chip;
+       girq->parent_handler = rp1_gpio_irq_handler;
+       girq->num_parents = RP1_NUM_BANKS;
+       girq->parents = pc->irq;
+       girq->default_type = IRQ_TYPE_NONE;
+       girq->handler = handle_level_irq;
+
+       /*
+        * Use the same handler for all groups: this is necessary
+        * since we use one gpiochip to cover all lines - the
+        * irq handler then needs to figure out which group and
+        * bank that was firing the IRQ and look up the per-group
+        * and bank data.
+        */
+       for (i = 0; i < RP1_NUM_BANKS; i++) {
+               pc->irq[i] = irq_of_parse_and_map(np, i);
+               if (!pc->irq[i]) {
+                       girq->num_parents = i;
+                       break;
+               }
+       }
+
+       platform_set_drvdata(pdev, pc);
+
+       err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
+       if (err)
+               return dev_err_probe(dev, err, "could not add GPIO chip\n");
+
+       return 0;
+}
+
+static struct platform_driver rp1_pinctrl_driver = {
+       .probe = rp1_pinctrl_probe,
+       .driver = {
+               .name = MODULE_NAME,
+               .of_match_table = rp1_pinctrl_match,
+               .suppress_bind_attrs = true,
+       },
+};
+module_platform_driver(rp1_pinctrl_driver);
+
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
+MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
+MODULE_DESCRIPTION("RP1 pinctrl/gpio driver");
+MODULE_LICENSE("GPL");