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 <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>
This commit is contained in:
Thomas Abraham 2016-04-23 22:18:08 +05:30 committed by Minkyu Kang
parent ac98527313
commit 16ca80adc5
7 changed files with 359 additions and 0 deletions

View File

@ -163,5 +163,6 @@ endif
source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
source "drivers/pinctrl/exynos/Kconfig"
endmenu

View 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/

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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
};