diff options
-rw-r--r-- | drivers/pinctrl/Kconfig | 1 | ||||
-rw-r--r-- | drivers/pinctrl/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/Makefile | 9 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/pinctrl-exynos.c | 141 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/pinctrl-exynos.h | 77 | ||||
-rw-r--r-- | drivers/pinctrl/exynos/pinctrl-exynos7420.c | 120 |
7 files changed, 359 insertions, 0 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 567b7662d0..1785e3b28c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -163,5 +163,6 @@ endif source "drivers/pinctrl/nxp/Kconfig" source "drivers/pinctrl/uniphier/Kconfig" +source "drivers/pinctrl/exynos/Kconfig" endmenu diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index b99ed2f191..7f946814d3 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -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 index 0000000000..84b6aaae09 --- /dev/null +++ b/drivers/pinctrl/exynos/Kconfig @@ -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 index 0000000000..d9b941ac67 --- /dev/null +++ b/drivers/pinctrl/exynos/Makefile @@ -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 index 0000000000..a28405fc15 --- /dev/null +++ b/drivers/pinctrl/exynos/pinctrl-exynos.c @@ -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 index 0000000000..abd582d318 --- /dev/null +++ b/drivers/pinctrl/exynos/pinctrl-exynos.h @@ -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 index 0000000000..8ae5ce776a --- /dev/null +++ b/drivers/pinctrl/exynos/pinctrl-exynos7420.c @@ -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 +}; |