pinctrl: add nexell driver
authorStefan Bosch <stefan_b@posteo.net>
Fri, 10 Jul 2020 17:07:30 +0000 (19:07 +0200)
committerTom Rini <trini@konsulko.com>
Wed, 29 Jul 2020 12:43:40 +0000 (08:43 -0400)
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- livetree API (dev_read_...) is used instead of fdt one (fdt...).
- doc/device-tree-bindings/pinctrl/nexell,s5pxx18-pinctrl.txt added.

Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
doc/device-tree-bindings/pinctrl/nexell,s5pxx18-pinctrl.txt [new file with mode: 0644]
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/nexell/Kconfig [new file with mode: 0644]
drivers/pinctrl/nexell/Makefile [new file with mode: 0644]
drivers/pinctrl/nexell/pinctrl-nexell.c [new file with mode: 0644]
drivers/pinctrl/nexell/pinctrl-nexell.h [new file with mode: 0644]
drivers/pinctrl/nexell/pinctrl-s5pxx18.c [new file with mode: 0644]
drivers/pinctrl/nexell/pinctrl-s5pxx18.h [new file with mode: 0644]

diff --git a/doc/device-tree-bindings/pinctrl/nexell,s5pxx18-pinctrl.txt b/doc/device-tree-bindings/pinctrl/nexell,s5pxx18-pinctrl.txt
new file mode 100644 (file)
index 0000000..115ab53
--- /dev/null
@@ -0,0 +1,78 @@
+Binding for Nexell s5pxx18 pin cotroller
+========================================
+
+Nexell's ARM bases SoC's integrates a GPIO and Pin mux/config hardware
+controller. It controls the input/output settings on the available pads/pins
+and also provides ability to multiplex and configure the output of various
+on-chip controllers onto these pads.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+
+Required properties:
+  - compatible: "nexell,s5pxx18-pinctrl"
+  - reg: should be register base and length as documented in the datasheet
+  - interrupts: interrupt specifier for the controller over gpio and alive pins
+
+Example:
+pinctrl_0: pinctrl@c0010000 {
+       compatible = "nexell,s5pxx18-pinctrl";
+               reg = <0xc0010000 0xf000>;
+               u-boot,dm-pre-reloc;
+};
+
+Nexell's pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters.
+
+  Child nodes must be set at least one of the following settings:
+  - pins = Select pins for using this function.
+  - pin-function = Select the function for use in a selected pin.
+  - pin-pull = Pull up/down configuration.
+  - pin-strength = Drive strength configuration.
+
+  Valid values for nexell,pins are:
+     "gpioX-N" : X in {A,B,C,D,E}, N in {0-31}
+  Valid values for nexell,pin-function are:
+     "N"       : N in {0-3}.
+                 This setting means that the value is different for each pin.
+                 Please refer to datasheet.
+  Valid values for nexell,pin-pull are:
+     "N"       : 0 - Down, 1 - Up, 2 - Off
+  Valid values for nexell,pin-strength are:
+     "N"       : 0,1,2,3
+
+
+Example:
+  - pin settings
+       mmc0_clk: mmc0-clk {
+               pins = "gpioa-29";
+               pin-function = <1>;
+               pin-pull = <2>;
+               pin-strength = <2>;
+       };
+
+       mmc0_cmd: mmc0-cmd {
+               pins = "gpioa-31";
+               pin-function = <1>;
+               pin-pull = <2>;
+               pin-strength = <1>;
+       };
+
+       mmc0_bus4: mmc0-bus-width4 {
+               pins = "gpiob-1, gpiob-3, gpiob-5, gpiob-7";
+               pin-function = <1>;
+               pin-pull = <2>;
+               pin-strength = <1>;
+       };
+
+  - used by client devices
+       mmc0:mmc@... {
+               pinctrl-names = "default";
+               pinctrl-0 = <&mmc0_clk>, <&mmc0_cmd>, <&mmc0_bus4>;
+               ...
+       };
index 83e39b9..bd2061b 100644 (file)
@@ -294,6 +294,7 @@ source "drivers/pinctrl/meson/Kconfig"
 source "drivers/pinctrl/mscc/Kconfig"
 source "drivers/pinctrl/mtmips/Kconfig"
 source "drivers/pinctrl/mvebu/Kconfig"
+source "drivers/pinctrl/nexell/Kconfig"
 source "drivers/pinctrl/nxp/Kconfig"
 source "drivers/pinctrl/renesas/Kconfig"
 source "drivers/pinctrl/rockchip/Kconfig"
index 4f662c4..92cff1b 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_PINCTRL_MESON)   += meson/
 obj-$(CONFIG_PINCTRL_MTK)      += mediatek/
 obj-$(CONFIG_PINCTRL_MSCC)     += mscc/
 obj-$(CONFIG_ARCH_MVEBU)       += mvebu/
+obj-$(CONFIG_ARCH_NEXELL)      += nexell/
 obj-$(CONFIG_PINCTRL_SINGLE)   += pinctrl-single.o
 obj-$(CONFIG_PINCTRL_STI)      += pinctrl-sti.o
 obj-$(CONFIG_PINCTRL_STM32)    += pinctrl_stm32.o
diff --git a/drivers/pinctrl/nexell/Kconfig b/drivers/pinctrl/nexell/Kconfig
new file mode 100644 (file)
index 0000000..8f1e472
--- /dev/null
@@ -0,0 +1,18 @@
+if ARCH_NEXELL
+
+config PINCTRL_NEXELL
+       bool "Nexell pinctrl driver"
+       help
+         Support of pin multiplexing and pin configuration for Nexell
+         SoCs.
+
+config PINCTRL_NEXELL_S5PXX18
+       bool "Nexell s5pxx18 SoC pinctrl driver"
+       default y if ARCH_S5P4418 || ARCH_S5P6818
+       depends on ARCH_NEXELL && PINCTRL_FULL
+       select PINCTRL_NEXELL
+       help
+         Support of pin multiplexing and pin configuration for S5P4418
+         and S5P6818 SoC.
+
+endif
diff --git a/drivers/pinctrl/nexell/Makefile b/drivers/pinctrl/nexell/Makefile
new file mode 100644 (file)
index 0000000..74df414
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier:     GPL-2.0+
+#
+# (C) Copyright 2016 Nexell
+# Bongyu, KOO <freestyle@nexell.co.kr>
+
+obj-$(CONFIG_PINCTRL_NEXELL) += pinctrl-nexell.o
+obj-$(CONFIG_PINCTRL_NEXELL_S5PXX18) += pinctrl-s5pxx18.o
diff --git a/drivers/pinctrl/nexell/pinctrl-nexell.c b/drivers/pinctrl/nexell/pinctrl-nexell.c
new file mode 100644 (file)
index 0000000..4518c05
--- /dev/null
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Pinctrl driver for Nexell SoCs
+ * (C) Copyright 2016 Nexell
+ * Bongyu, KOO <freestyle@nexell.co.kr>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include "pinctrl-nexell.h"
+#include "pinctrl-s5pxx18.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* given a pin-name, return the address of pin config registers */
+unsigned long pin_to_bank_base(struct udevice *dev, const char *pin_name,
+                              u32 *pin)
+{
+       struct nexell_pinctrl_priv *priv = dev_get_priv(dev);
+       const struct nexell_pin_ctrl *pin_ctrl = priv->pin_ctrl;
+       const struct nexell_pin_bank_data *bank_data = pin_ctrl->pin_banks;
+       u32 nr_banks = pin_ctrl->nr_banks, idx = 0;
+       char bank[10];
+
+       /*
+        * The format of the pin name is <bank name>-<pin_number>.
+        * Example: gpioa-4 (gpioa is the bank name and 4 is the pin number)
+        */
+       while (pin_name[idx] != '-') {
+               bank[idx] = pin_name[idx];
+               idx++;
+       }
+       bank[idx] = '\0';
+       *pin = (u32)simple_strtoul(&pin_name[++idx], NULL, 10);
+
+       /* lookup the pin bank data using the pin bank name */
+       for (idx = 0; idx < nr_banks; idx++)
+               if (!strcmp(bank, bank_data[idx].name))
+                       break;
+
+       return priv->base + bank_data[idx].offset;
+}
+
+int nexell_pinctrl_probe(struct udevice *dev)
+{
+       struct nexell_pinctrl_priv *priv;
+       fdt_addr_t base;
+
+       priv = dev_get_priv(dev);
+       if (!priv)
+               return -EINVAL;
+
+       base = devfdt_get_addr(dev);
+       if (base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       priv->base = base;
+
+       priv->pin_ctrl = (struct nexell_pin_ctrl *)dev_get_driver_data(dev);
+
+       s5pxx18_pinctrl_init(dev);
+
+       return 0;
+}
diff --git a/drivers/pinctrl/nexell/pinctrl-nexell.h b/drivers/pinctrl/nexell/pinctrl-nexell.h
new file mode 100644 (file)
index 0000000..b21eefc
--- /dev/null
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Pinctrl driver for Nexell SoCs
+ * (C) Copyright 2016 Nexell
+ * Bongyu, KOO <freestyle@nexell.co.kr>
+ *
+ */
+
+#ifndef __PINCTRL_NEXELL_H_
+#define __PINCTRL_NEXELL_H_
+
+/**
+ * struct nexell_pin_bank_data: represent a controller pin-bank data.
+ * @offset: starting offset of the pin-bank registers.
+ * @nr_pins: number of pins included in this bank.
+ * @name: name to be prefixed for each pin in this pin bank.
+ */
+struct nexell_pin_bank_data {
+       u32             offset;
+       u8              nr_pins;
+       const char      *name;
+       u8              type;
+};
+
+#define NEXELL_PIN_BANK(pins, reg, id)                 \
+       {                                               \
+               .offset         = reg,                  \
+               .nr_pins        = pins,                 \
+               .name           = id                    \
+       }
+
+/**
+ * struct nexell_pin_ctrl: represent a pin controller.
+ * @pin_banks: list of pin banks included in this controller.
+ * @nr_banks: number of pin banks.
+ */
+struct nexell_pin_ctrl {
+       const struct nexell_pin_bank_data *pin_banks;
+       u32 nr_banks;
+};
+
+/**
+ * struct nexell_pinctrl_priv: nexell pin controller driver private data
+ * @pin_ctrl: pin controller bank information.
+ * @base: base address of the pin controller instance.
+ */
+struct nexell_pinctrl_priv {
+       const struct nexell_pin_ctrl *pin_ctrl;
+       unsigned long base;
+};
+
+/**
+ * struct nexell_pinctrl_config_data: configuration for a peripheral.
+ * @offset: offset of the config registers in the controller.
+ * @mask: value of the register to be masked with.
+ * @value: new value to be programmed.
+ */
+struct nexell_pinctrl_config_data {
+       const unsigned int      offset;
+       const unsigned int      mask;
+       const unsigned int      value;
+};
+
+unsigned long pin_to_bank_base(struct udevice *dev, const char *pin_name,
+                              u32 *pin);
+int nexell_pinctrl_probe(struct udevice *dev);
+
+#endif /* __PINCTRL_NEXELL_H_ */
diff --git a/drivers/pinctrl/nexell/pinctrl-s5pxx18.c b/drivers/pinctrl/nexell/pinctrl-s5pxx18.c
new file mode 100644 (file)
index 0000000..96a2ed3
--- /dev/null
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Pinctrl driver for Nexell SoCs
+ * (C) Copyright 2016 Nexell
+ * Bongyu, KOO <freestyle@nexell.co.kr>
+ *
+ * (C) Copyright 2019 Stefan Bosch <stefan_b@posteo.net>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include "pinctrl-nexell.h"
+#include "pinctrl-s5pxx18.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void nx_gpio_set_bit(u32 *value, u32 bit, int enable)
+{
+       register u32 newvalue;
+
+       newvalue = *value;
+       newvalue &= ~(1ul << bit);
+       newvalue |= (u32)enable << bit;
+       writel(newvalue, value);
+}
+
+static void nx_gpio_set_bit2(u32 *value, u32 bit, u32 bit_value)
+{
+       register u32 newvalue = *value;
+
+       newvalue = (u32)(newvalue & ~(3ul << (bit * 2)));
+       newvalue = (u32)(newvalue | (bit_value << (bit * 2)));
+
+       writel(newvalue, value);
+}
+
+static int nx_gpio_open_module(void *base)
+{
+       writel(0xFFFFFFFF, base + GPIOX_SLEW_DISABLE_DEFAULT);
+       writel(0xFFFFFFFF, base + GPIOX_DRV1_DISABLE_DEFAULT);
+       writel(0xFFFFFFFF, base + GPIOX_DRV0_DISABLE_DEFAULT);
+       writel(0xFFFFFFFF, base + GPIOX_PULLSEL_DISABLE_DEFAULT);
+       writel(0xFFFFFFFF, base + GPIOX_PULLENB_DISABLE_DEFAULT);
+       return true;
+}
+
+static void nx_gpio_set_pad_function(void *base, u32 pin, u32 padfunc)
+{
+       u32 reg = (pin / 16) ? GPIOX_ALTFN1 : GPIOX_ALTFN0;
+
+       nx_gpio_set_bit2(base + reg, pin % 16, padfunc);
+}
+
+static void nx_gpio_set_drive_strength(void *base, u32 pin, u32 drv)
+{
+       nx_gpio_set_bit(base + GPIOX_DRV1, pin, (int)(((u32)drv >> 0) & 0x1));
+       nx_gpio_set_bit(base + GPIOX_DRV0, pin, (int)(((u32)drv >> 1) & 0x1));
+}
+
+static void nx_gpio_set_pull_mode(void *base, u32 pin, u32 mode)
+{
+       if (mode == nx_gpio_pull_off) {
+               nx_gpio_set_bit(base + GPIOX_PULLENB, pin, false);
+               nx_gpio_set_bit(base + GPIOX_PULLSEL, pin, false);
+       } else {
+               nx_gpio_set_bit(base + GPIOX_PULLSEL,
+                               pin, (mode & 1 ? true : false));
+               nx_gpio_set_bit(base + GPIOX_PULLENB, pin, true);
+       }
+}
+
+static void nx_alive_set_pullup(void *base, u32 pin, bool enable)
+{
+       u32 PULLUP_MASK;
+
+       PULLUP_MASK = (1UL << pin);
+       if (enable)
+               writel(PULLUP_MASK, base + ALIVE_PADPULLUPSET);
+       else
+               writel(PULLUP_MASK, base + ALIVE_PADPULLUPRST);
+}
+
+static int s5pxx18_pinctrl_gpio_init(struct udevice *dev)
+{
+       struct nexell_pinctrl_priv *priv = dev_get_priv(dev);
+       const struct nexell_pin_ctrl *ctrl = priv->pin_ctrl;
+       unsigned long reg = priv->base;
+       int i;
+
+       for (i = 0; i < ctrl->nr_banks - 1; i++) /* except alive bank */
+               nx_gpio_open_module((void *)(reg + ctrl->pin_banks[i].offset));
+
+       return 0;
+}
+
+static int s5pxx18_pinctrl_alive_init(struct udevice *dev)
+{
+       struct nexell_pinctrl_priv *priv = dev_get_priv(dev);
+       const struct nexell_pin_ctrl *ctrl = priv->pin_ctrl;
+       unsigned long reg = priv->base;
+
+       reg += ctrl->pin_banks[ctrl->nr_banks - 1].offset;
+
+       writel(1, reg + ALIVE_PWRGATE);
+       return 0;
+}
+
+int s5pxx18_pinctrl_init(struct udevice *dev)
+{
+       s5pxx18_pinctrl_gpio_init(dev);
+       s5pxx18_pinctrl_alive_init(dev);
+
+       return 0;
+}
+
+static int is_pin_alive(const char *name)
+{
+       return !strncmp(name, "alive", 5);
+}
+
+/**
+ * s5pxx18_pinctrl_set_state: configure a pin state.
+ * dev: the pinctrl device to be configured.
+ * config: the state to be configured.
+ */
+static int s5pxx18_pinctrl_set_state(struct udevice *dev,
+                                    struct udevice *config)
+{
+       unsigned int count, idx, pin;
+       unsigned int pinfunc, pinpud, pindrv;
+       unsigned long reg;
+       const char *name;
+       int ret;
+
+       /*
+        * refer to the following document for the pinctrl bindings
+        * doc/device-tree-bindings/pinctrl/nexell,s5pxx18-pinctrl.txt
+        */
+       count = dev_read_string_count(config, "pins");
+
+       if (count <= 0)
+               return -EINVAL;
+
+       pinfunc = dev_read_s32_default(config, "pin-function", -1);
+       pinpud = dev_read_s32_default(config, "pin-pull", -1);
+       pindrv = dev_read_s32_default(config, "pin-strength", -1);
+
+       for (idx = 0; idx < count; idx++) {
+               ret = dev_read_string_index(config, "pins", idx, &name);
+               if (ret)
+                       return ret;
+               if (!name)
+                       continue;
+               reg = pin_to_bank_base(dev, name, &pin);
+
+               if (is_pin_alive(name)) {
+                       /* pin pull up/down */
+                       if (pinpud != -1)
+                               nx_alive_set_pullup((void *)reg, pin,
+                                                   pinpud & 1);
+                       continue;
+               }
+
+               /* pin function */
+               if (pinfunc != -1)
+                       nx_gpio_set_pad_function((void *)reg, pin, pinfunc);
+
+               /* pin pull up/down/off */
+               if (pinpud != -1)
+                       nx_gpio_set_pull_mode((void *)reg, pin, pinpud);
+
+               /* pin drive strength */
+               if (pindrv != -1)
+                       nx_gpio_set_drive_strength((void *)reg, pin, pindrv);
+       }
+
+       return 0;
+}
+
+static struct pinctrl_ops s5pxx18_pinctrl_ops = {
+       .set_state      = s5pxx18_pinctrl_set_state,
+};
+
+/* pin banks of s5pxx18 pin-controller */
+static const struct nexell_pin_bank_data s5pxx18_pin_banks[] = {
+       NEXELL_PIN_BANK(32, 0xA000, "gpioa"),
+       NEXELL_PIN_BANK(32, 0xB000, "gpiob"),
+       NEXELL_PIN_BANK(32, 0xC000, "gpioc"),
+       NEXELL_PIN_BANK(32, 0xD000, "gpiod"),
+       NEXELL_PIN_BANK(32, 0xE000, "gpioe"),
+       NEXELL_PIN_BANK(6, 0x0800, "alive"),
+};
+
+const struct nexell_pin_ctrl s5pxx18_pin_ctrl[] = {
+       {
+               /* pin-controller data */
+               .pin_banks      = s5pxx18_pin_banks,
+               .nr_banks       = ARRAY_SIZE(s5pxx18_pin_banks),
+       },
+};
+
+static const struct udevice_id s5pxx18_pinctrl_ids[] = {
+       { .compatible = "nexell,s5pxx18-pinctrl",
+               .data = (ulong)s5pxx18_pin_ctrl },
+       { }
+};
+
+U_BOOT_DRIVER(pinctrl_s5pxx18) = {
+       .name           = "pinctrl_s5pxx18",
+       .id             = UCLASS_PINCTRL,
+       .of_match       = s5pxx18_pinctrl_ids,
+       .priv_auto_alloc_size = sizeof(struct nexell_pinctrl_priv),
+       .ops            = &s5pxx18_pinctrl_ops,
+       .probe          = nexell_pinctrl_probe,
+       .flags          = DM_FLAG_PRE_RELOC
+};
diff --git a/drivers/pinctrl/nexell/pinctrl-s5pxx18.h b/drivers/pinctrl/nexell/pinctrl-s5pxx18.h
new file mode 100644 (file)
index 0000000..843a00b
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Pinctrl driver for Nexell SoCs
+ * (C) Copyright 2016 Nexell
+ * Bongyu, KOO <freestyle@nexell.co.kr>
+ */
+
+#ifndef __PINCTRL_S5PXX18_H_
+#define __PINCTRL_S5PXX18_H_
+
+#include <linux/types.h>
+#include <asm/io.h>
+
+#define GPIOX_ALTFN0   0x20
+#define GPIOX_ALTFN1   0x24
+#define GPIOX_DRV1     0x48
+#define GPIOX_DRV0     0x50
+#define GPIOX_PULLSEL  0x58
+#define GPIOX_PULLENB  0x60
+
+#define GPIOX_SLEW_DISABLE_DEFAULT     0x44
+#define GPIOX_DRV1_DISABLE_DEFAULT     0x4C
+#define GPIOX_DRV0_DISABLE_DEFAULT     0x54
+#define GPIOX_PULLSEL_DISABLE_DEFAULT  0x5C
+#define GPIOX_PULLENB_DISABLE_DEFAULT  0x64
+
+#define ALIVE_PWRGATE                  0x0
+#define ALIVE_PADPULLUPRST             0x80
+#define ALIVE_PADPULLUPSET             0x84
+#define ALIVE_PADPULLUPREAD            0x88
+
+enum {
+       nx_gpio_padfunc_0 = 0ul,
+       nx_gpio_padfunc_1 = 1ul,
+       nx_gpio_padfunc_2 = 2ul,
+       nx_gpio_padfunc_3 = 3ul
+};
+
+enum {
+       nx_gpio_drvstrength_0 = 0ul,
+       nx_gpio_drvstrength_1 = 1ul,
+       nx_gpio_drvstrength_2 = 2ul,
+       nx_gpio_drvstrength_3 = 3ul
+};
+
+enum {
+       nx_gpio_pull_down = 0ul,
+       nx_gpio_pull_up = 1ul,
+       nx_gpio_pull_off = 2ul
+};
+
+int s5pxx18_pinctrl_init(struct udevice *dev);
+#endif /* __PINCTRL_S5PXX18_H_ */