]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
pinctrl: Add pinctrl driver support for Exynos7420 SoC
authorThomas Abraham <thomas.ab@samsung.com>
Sat, 23 Apr 2016 16:48:08 +0000 (22:18 +0530)
committerMinkyu Kang <mk7.kang@samsung.com>
Wed, 25 May 2016 01:00:18 +0000 (10:00 +0900)
Add pinctrl driver support for Samsung's Exynos7420 SoC. The changes
have been split into Exynos7420 specific and common Exynos specific
portions so that this implementation is reusable on other Exynos
SoCs as well.

The Exynos pinctrl driver supports only device tree based pin
configuration. The bindings used are similar to the ones used in the
linux kernel.

Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Simon Glass <sjg@chromium.org>
Cc: Minkyu Kang <mk7.kang@samsung.com>
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Acked-by: Minkyu Kang <mk7.kang@samsung.com>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/exynos/Kconfig [new file with mode: 0644]
drivers/pinctrl/exynos/Makefile [new file with mode: 0644]
drivers/pinctrl/exynos/pinctrl-exynos.c [new file with mode: 0644]
drivers/pinctrl/exynos/pinctrl-exynos.h [new file with mode: 0644]
drivers/pinctrl/exynos/pinctrl-exynos7420.c [new file with mode: 0644]

index 567b7662d00d915e309fb029316ba52a60aa8271..1785e3b28cf5cabd962017e8bb173e27d2ec4e99 100644 (file)
@@ -163,5 +163,6 @@ endif
 
 source "drivers/pinctrl/nxp/Kconfig"
 source "drivers/pinctrl/uniphier/Kconfig"
+source "drivers/pinctrl/exynos/Kconfig"
 
 endmenu
index b99ed2f1916f338baafdc7abd252f22bcd52b976..7f946814d33f79d05a991cdbb862c3c96f374195 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
 
 obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
 obj-$(CONFIG_PIC32_PINCTRL)    += pinctrl_pic32.o
+obj-$(CONFIG_PINCTRL_EXYNOS)   += exynos/
diff --git a/drivers/pinctrl/exynos/Kconfig b/drivers/pinctrl/exynos/Kconfig
new file mode 100644 (file)
index 0000000..84b6aaa
--- /dev/null
@@ -0,0 +1,10 @@
+config PINCTRL_EXYNOS
+       bool
+
+config PINCTRL_EXYNOS7420
+       bool "Samsung Exynos7420 pinctrl driver"
+       depends on ARCH_EXYNOS && PINCTRL_FULL
+       select PINCTRL_EXYNOS
+       help
+         Support pin multiplexing and pin configuration control on
+         Samsung's Exynos7420 SoC.
diff --git a/drivers/pinctrl/exynos/Makefile b/drivers/pinctrl/exynos/Makefile
new file mode 100644 (file)
index 0000000..d9b941a
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2016 Samsung Electronics
+# Thomas Abraham <thomas.ab@samsung.com>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-$(CONFIG_PINCTRL_EXYNOS)           += pinctrl-exynos.o
+obj-$(CONFIG_PINCTRL_EXYNOS7420)       += pinctrl-exynos7420.o
diff --git a/drivers/pinctrl/exynos/pinctrl-exynos.c b/drivers/pinctrl/exynos/pinctrl-exynos.c
new file mode 100644 (file)
index 0000000..a28405f
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Exynos pinctrl driver common code.
+ * Copyright (C) 2016 Samsung Electronics
+ * Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include "pinctrl-exynos.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * exynos_pinctrl_setup_peri: setup pinctrl for a peripheral.
+ * conf: soc specific pin configuration data array
+ * num_conf: number of configurations in the conf array.
+ * base: base address of the pin controller.
+ */
+void exynos_pinctrl_setup_peri(struct exynos_pinctrl_config_data *conf,
+               unsigned int num_conf, unsigned long base)
+{
+       unsigned int idx, val;
+
+       for (idx = 0; idx < num_conf; idx++) {
+               val = readl(base + conf[idx].offset);
+               val &= ~(conf[idx].mask);
+               val |= conf[idx].value;
+               writel(val, base + conf[idx].offset);
+       }
+}
+
+/* given a pin-name, return the address of pin config registers */
+static unsigned long pin_to_bank_base(struct udevice *dev, const char *pin_name,
+                                               u32 *pin)
+{
+       struct exynos_pinctrl_priv *priv = dev_get_priv(dev);
+       const struct samsung_pin_ctrl *pin_ctrl = priv->pin_ctrl;
+       const struct samsung_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: gpa0-4 (gpa0 is the bank name and 4 is the pin number.
+        */
+       while (pin_name[idx] != '-') {
+               bank[idx] = pin_name[idx];
+               idx++;
+       }
+       bank[idx] = '\0';
+       *pin = pin_name[++idx] - '0';
+
+       /* 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;
+}
+
+/**
+ * exynos_pinctrl_set_state: configure a pin state.
+ * dev: the pinctrl device to be configured.
+ * config: the state to be configured.
+ */
+int exynos_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+       const void *fdt = gd->fdt_blob;
+       int node = config->of_offset;
+       unsigned int count, idx, pin_num, ret;
+       unsigned int pinfunc, pinpud, pindrv;
+       unsigned long reg, value;
+       const char *name;
+
+       /*
+        * refer to the following document for the pinctrl bindings
+        * linux/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
+        */
+       count = fdt_count_strings(fdt, node, "samsung,pins");
+       if (count <= 0)
+               return -EINVAL;
+
+       pinfunc = fdtdec_get_int(fdt, node, "samsung,pin-function", -1);
+       pinpud = fdtdec_get_int(fdt, node, "samsung,pin-pud", -1);
+       pindrv = fdtdec_get_int(fdt, node, "samsung,pin-drv", -1);
+
+       for (idx = 0; idx < count; idx++) {
+               ret = fdt_get_string_index(fdt, node, "samsung,pins",
+                                               idx, &name);
+               if (ret < 0)
+                       continue;
+               reg = pin_to_bank_base(dev, name, &pin_num);
+
+               if (pinfunc != -1) {
+                       value = readl(reg + PIN_CON);
+                       value &= ~(0xf << (pin_num << 2));
+                       value |= (pinfunc << (pin_num << 2));
+                       writel(value, reg + PIN_CON);
+               }
+
+               if (pinpud != -1) {
+                       value = readl(reg + PIN_PUD);
+                       value &= ~(0x3 << (pin_num << 1));
+                       value |= (pinpud << (pin_num << 1));
+                       writel(value, reg + PIN_PUD);
+               }
+
+               if (pindrv != -1) {
+                       value = readl(reg + PIN_DRV);
+                       value &= ~(0x3 << (pin_num << 1));
+                       value |= (pindrv << (pin_num << 1));
+                       writel(value, reg + PIN_DRV);
+               }
+       }
+
+       return 0;
+}
+
+int exynos_pinctrl_probe(struct udevice *dev)
+{
+       struct exynos_pinctrl_priv *priv;
+       fdt_addr_t base;
+
+       priv = dev_get_priv(dev);
+       if (!priv)
+               return -EINVAL;
+
+       base = dev_get_addr(dev);
+       if (base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       priv->base = base;
+       priv->pin_ctrl = (struct samsung_pin_ctrl *)dev_get_driver_data(dev) +
+                               dev->req_seq;
+
+       return 0;
+}
diff --git a/drivers/pinctrl/exynos/pinctrl-exynos.h b/drivers/pinctrl/exynos/pinctrl-exynos.h
new file mode 100644 (file)
index 0000000..abd582d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Exynos pinctrl driver header.
+ * Copyright (C) 2016 Samsung Electronics
+ * Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __PINCTRL_EXYNOS_H_
+#define __PINCTRL_EXYNOS__H_
+
+#define PIN_CON                0x00    /* Offset of pin function register */
+#define PIN_DAT                0x04    /* Offset of pin data register */
+#define PIN_PUD                0x08    /* Offset of pin pull up/down config register */
+#define PIN_DRV                0x0C    /* Offset of pin drive strength register */
+
+/**
+ * struct samsung_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 samsung_pin_bank_data {
+       u32             offset;
+       u8              nr_pins;
+       const char      *name;
+};
+
+#define EXYNOS_PIN_BANK(pins, reg, id)                 \
+       {                                               \
+               .offset = reg,                          \
+               .nr_pins        = pins,                 \
+               .name           = id                    \
+       }
+
+/**
+ * struct samsung_pin_ctrl: represent a pin controller.
+ * @pin_banks: list of pin banks included in this controller.
+ * @nr_banks: number of pin banks.
+ */
+struct samsung_pin_ctrl {
+       const struct samsung_pin_bank_data *pin_banks;
+       u32 nr_banks;
+};
+
+/**
+ * struct exynos_pinctrl_priv: exynos pin controller driver private data
+ * @pin_ctrl: pin controller bank information.
+ * @base: base address of the pin controller instance.
+ * @num_banks: number of pin banks included in the pin controller.
+ */
+struct exynos_pinctrl_priv {
+       const struct samsung_pin_ctrl *pin_ctrl;
+       unsigned long base;
+       int num_banks;
+};
+
+/**
+ * struct exynos_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 exynos_pinctrl_config_data {
+       const unsigned int      offset;
+       const unsigned int      mask;
+       const unsigned int      value;
+};
+
+
+void exynos_pinctrl_setup_peri(struct exynos_pinctrl_config_data *conf,
+               unsigned int num_conf, unsigned long base);
+int exynos_pinctrl_set_state(struct udevice *dev,
+               struct udevice *config);
+int exynos_pinctrl_probe(struct udevice *dev);
+
+#endif /* __PINCTRL_EXYNOS_H_ */
diff --git a/drivers/pinctrl/exynos/pinctrl-exynos7420.c b/drivers/pinctrl/exynos/pinctrl-exynos7420.c
new file mode 100644 (file)
index 0000000..8ae5ce7
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Exynos7420 pinctrl driver.
+ * Copyright (C) 2016 Samsung Electronics
+ * Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <fdtdec.h>
+#include <asm/arch/pinmux.h>
+#include "pinctrl-exynos.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define        GPD1_OFFSET     0xc0
+
+static struct exynos_pinctrl_config_data serial2_conf[] = {
+       {
+               .offset = GPD1_OFFSET + PIN_CON,
+               .mask   = 0x00ff0000,
+               .value  = 0x00220000,
+       }, {
+               .offset = GPD1_OFFSET + PIN_PUD,
+               .mask   = 0x00000f00,
+               .value  = 0x00000f00,
+       },
+};
+
+static int exynos7420_pinctrl_request(struct udevice *dev, int peripheral,
+                                               int flags)
+{
+       struct exynos_pinctrl_priv *priv = dev_get_priv(dev);
+       unsigned long base = priv->base;
+
+       switch (PERIPH_ID_UART2) {
+       case PERIPH_ID_UART2:
+               exynos_pinctrl_setup_peri(serial2_conf,
+                                         ARRAY_SIZE(serial2_conf), base);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct pinctrl_ops exynos7420_pinctrl_ops = {
+       .set_state      = exynos_pinctrl_set_state,
+       .request        = exynos7420_pinctrl_request,
+};
+
+/* pin banks of Exynos7420 pin-controller - BUS0 */
+static const struct samsung_pin_bank_data exynos7420_pin_banks0[] = {
+       EXYNOS_PIN_BANK(5, 0x000, "gpb0"),
+       EXYNOS_PIN_BANK(8, 0x020, "gpc0"),
+       EXYNOS_PIN_BANK(2, 0x040, "gpc1"),
+       EXYNOS_PIN_BANK(6, 0x060, "gpc2"),
+       EXYNOS_PIN_BANK(8, 0x080, "gpc3"),
+       EXYNOS_PIN_BANK(4, 0x0a0, "gpd0"),
+       EXYNOS_PIN_BANK(6, 0x0c0, "gpd1"),
+       EXYNOS_PIN_BANK(8, 0x0e0, "gpd2"),
+       EXYNOS_PIN_BANK(5, 0x100, "gpd4"),
+       EXYNOS_PIN_BANK(4, 0x120, "gpd5"),
+       EXYNOS_PIN_BANK(6, 0x140, "gpd6"),
+       EXYNOS_PIN_BANK(3, 0x160, "gpd7"),
+       EXYNOS_PIN_BANK(2, 0x180, "gpd8"),
+       EXYNOS_PIN_BANK(2, 0x1a0, "gpg0"),
+       EXYNOS_PIN_BANK(4, 0x1c0, "gpg3"),
+};
+
+/* pin banks of Exynos7420 pin-controller - FSYS0 */
+static const struct samsung_pin_bank_data exynos7420_pin_banks1[] = {
+       EXYNOS_PIN_BANK(7, 0x000, "gpr4"),
+};
+
+/* pin banks of Exynos7420 pin-controller - FSYS1 */
+static const struct samsung_pin_bank_data exynos7420_pin_banks2[] = {
+       EXYNOS_PIN_BANK(4, 0x000, "gpr0"),
+       EXYNOS_PIN_BANK(8, 0x020, "gpr1"),
+       EXYNOS_PIN_BANK(5, 0x040, "gpr2"),
+       EXYNOS_PIN_BANK(8, 0x060, "gpr3"),
+};
+
+const struct samsung_pin_ctrl exynos7420_pin_ctrl[] = {
+       {
+               /* pin-controller instance BUS0 data */
+               .pin_banks      = exynos7420_pin_banks0,
+               .nr_banks       = ARRAY_SIZE(exynos7420_pin_banks0),
+       }, {
+               /* pin-controller instance FSYS0 data */
+               .pin_banks      = exynos7420_pin_banks1,
+               .nr_banks       = ARRAY_SIZE(exynos7420_pin_banks1),
+       }, {
+               /* pin-controller instance FSYS1 data */
+               .pin_banks      = exynos7420_pin_banks2,
+               .nr_banks       = ARRAY_SIZE(exynos7420_pin_banks2),
+       },
+};
+
+static const struct udevice_id exynos7420_pinctrl_ids[] = {
+       { .compatible = "samsung,exynos7420-pinctrl",
+               .data = (ulong)exynos7420_pin_ctrl },
+       { }
+};
+
+U_BOOT_DRIVER(pinctrl_exynos7420) = {
+       .name           = "pinctrl_exynos7420",
+       .id             = UCLASS_PINCTRL,
+       .of_match       = exynos7420_pinctrl_ids,
+       .priv_auto_alloc_size = sizeof(struct exynos_pinctrl_priv),
+       .ops            = &exynos7420_pinctrl_ops,
+       .probe          = exynos_pinctrl_probe,
+       .flags          = DM_FLAG_PRE_RELOC
+};