blob: 96a2ed38a056eb88cf62cd3f9152344e7f198a9f [file] [log] [blame]
// 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
};