From: Thomas Abraham Date: Sat, 23 Apr 2016 16:48:08 +0000 (+0530) Subject: pinctrl: Add pinctrl driver support for Exynos7420 SoC X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=16ca80adc551808b6be1d43f30997f8b4fdfbd39;p=u-boot.git pinctrl: Add pinctrl driver support for Exynos7420 SoC 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 Cc: Simon Glass Cc: Minkyu Kang Signed-off-by: Thomas Abraham Reviewed-by: Simon Glass Acked-by: Minkyu Kang Signed-off-by: Minkyu Kang --- 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 +# +# 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 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#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 -. + * 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 + * + * 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 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 +};