233 lines
5.6 KiB
C
233 lines
5.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2019-2020, Broadcom
|
||
|
*
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*/
|
||
|
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include <drivers/gpio.h>
|
||
|
#include <lib/mmio.h>
|
||
|
#include <plat/common/platform.h>
|
||
|
|
||
|
#include <iproc_gpio.h>
|
||
|
#include <platform_def.h>
|
||
|
|
||
|
#define IPROC_GPIO_DATA_IN_OFFSET 0x00
|
||
|
#define IPROC_GPIO_DATA_OUT_OFFSET 0x04
|
||
|
#define IPROC_GPIO_OUT_EN_OFFSET 0x08
|
||
|
#define IPROC_GPIO_PAD_RES_OFFSET 0x34
|
||
|
#define IPROC_GPIO_RES_EN_OFFSET 0x38
|
||
|
|
||
|
#define PINMUX_OFFSET(gpio) ((gpio) * 4)
|
||
|
#define PINCONF_OFFSET(gpio) ((gpio) * 4)
|
||
|
#define PINCONF_PULL_UP BIT(4)
|
||
|
#define PINCONF_PULL_DOWN BIT(5)
|
||
|
|
||
|
/*
|
||
|
* iProc GPIO bank is always 0x200 per bank,
|
||
|
* with each bank supporting 32 GPIOs.
|
||
|
*/
|
||
|
#define GPIO_BANK_SIZE 0x200
|
||
|
#define NGPIOS_PER_BANK 32
|
||
|
#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
|
||
|
|
||
|
#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
|
||
|
#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
|
||
|
|
||
|
#define MUX_GPIO_MODE 0x3
|
||
|
|
||
|
/*
|
||
|
* @base: base address of the gpio controller
|
||
|
* @pinconf_base: base address of the pinconf
|
||
|
* @pinmux_base: base address of the mux controller
|
||
|
* @nr_gpios: maxinum number of GPIOs
|
||
|
*/
|
||
|
struct iproc_gpio {
|
||
|
uintptr_t base;
|
||
|
uintptr_t pinconf_base;
|
||
|
uintptr_t pinmux_base;
|
||
|
int nr_gpios;
|
||
|
};
|
||
|
|
||
|
static struct iproc_gpio iproc_gpio;
|
||
|
|
||
|
static void gpio_set_bit(uintptr_t base, unsigned int reg, int gpio, bool set)
|
||
|
{
|
||
|
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
|
||
|
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
|
||
|
uint32_t val;
|
||
|
|
||
|
val = mmio_read_32(base + offset);
|
||
|
if (set)
|
||
|
val |= BIT(shift);
|
||
|
else
|
||
|
val &= ~BIT(shift);
|
||
|
|
||
|
mmio_write_32(base + offset, val);
|
||
|
}
|
||
|
|
||
|
static bool gpio_get_bit(uintptr_t base, unsigned int reg, int gpio)
|
||
|
{
|
||
|
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
|
||
|
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
|
||
|
|
||
|
return !!(mmio_read_32(base + offset) & BIT(shift));
|
||
|
}
|
||
|
|
||
|
static void mux_to_gpio(struct iproc_gpio *g, int gpio)
|
||
|
{
|
||
|
/* mux pad to GPIO if IOPAD configuration is mandatory */
|
||
|
if (g->pinmux_base)
|
||
|
mmio_write_32(g->pinmux_base + PINMUX_OFFSET(gpio),
|
||
|
MUX_GPIO_MODE);
|
||
|
}
|
||
|
|
||
|
static void set_direction(int gpio, int direction)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
bool dir = (direction == GPIO_DIR_OUT) ? true : false;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
|
||
|
mux_to_gpio(g, gpio);
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, dir);
|
||
|
}
|
||
|
|
||
|
static int get_direction(int gpio)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
int dir;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
|
||
|
mux_to_gpio(g, gpio);
|
||
|
dir = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
|
||
|
GPIO_DIR_OUT : GPIO_DIR_IN;
|
||
|
|
||
|
return dir;
|
||
|
}
|
||
|
|
||
|
static int get_value(int gpio)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
unsigned int offset;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
|
||
|
mux_to_gpio(g, gpio);
|
||
|
|
||
|
/*
|
||
|
* If GPIO is configured as output, read from the GPIO_OUT register;
|
||
|
* otherwise, read from the GPIO_IN register
|
||
|
*/
|
||
|
offset = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
|
||
|
IPROC_GPIO_DATA_OUT_OFFSET : IPROC_GPIO_DATA_IN_OFFSET;
|
||
|
|
||
|
return gpio_get_bit(g->base, offset, gpio);
|
||
|
}
|
||
|
|
||
|
static void set_value(int gpio, int val)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
|
||
|
mux_to_gpio(g, gpio);
|
||
|
|
||
|
/* make sure GPIO is configured to output, and then set the value */
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, true);
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
|
||
|
}
|
||
|
|
||
|
static int get_pull(int gpio)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
uint32_t val;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
mux_to_gpio(g, gpio);
|
||
|
|
||
|
/* when there's a valid pinconf_base, use it */
|
||
|
if (g->pinconf_base) {
|
||
|
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
|
||
|
|
||
|
if (val & PINCONF_PULL_UP)
|
||
|
return GPIO_PULL_UP;
|
||
|
else if (val & PINCONF_PULL_DOWN)
|
||
|
return GPIO_PULL_DOWN;
|
||
|
else
|
||
|
return GPIO_PULL_NONE;
|
||
|
}
|
||
|
|
||
|
/* no pinconf_base. fall back to GPIO internal pull control */
|
||
|
if (!gpio_get_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio))
|
||
|
return GPIO_PULL_NONE;
|
||
|
|
||
|
return gpio_get_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio) ?
|
||
|
GPIO_PULL_UP : GPIO_PULL_DOWN;
|
||
|
}
|
||
|
|
||
|
static void set_pull(int gpio, int pull)
|
||
|
{
|
||
|
struct iproc_gpio *g = &iproc_gpio;
|
||
|
uint32_t val;
|
||
|
|
||
|
assert(gpio < g->nr_gpios);
|
||
|
mux_to_gpio(g, gpio);
|
||
|
|
||
|
/* when there's a valid pinconf_base, use it */
|
||
|
if (g->pinconf_base) {
|
||
|
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
|
||
|
|
||
|
if (pull == GPIO_PULL_NONE) {
|
||
|
val &= ~(PINCONF_PULL_UP | PINCONF_PULL_DOWN);
|
||
|
} else if (pull == GPIO_PULL_UP) {
|
||
|
val |= PINCONF_PULL_UP;
|
||
|
val &= ~PINCONF_PULL_DOWN;
|
||
|
} else if (pull == GPIO_PULL_DOWN) {
|
||
|
val |= PINCONF_PULL_DOWN;
|
||
|
val &= ~PINCONF_PULL_UP;
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
mmio_write_32(g->pinconf_base + PINCONF_OFFSET(gpio), val);
|
||
|
}
|
||
|
|
||
|
/* no pinconf_base. fall back to GPIO internal pull control */
|
||
|
if (pull == GPIO_PULL_NONE) {
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* enable pad register and pull up or down */
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
|
||
|
gpio_set_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio,
|
||
|
!!(pull == GPIO_PULL_UP));
|
||
|
}
|
||
|
|
||
|
const gpio_ops_t iproc_gpio_ops = {
|
||
|
.get_direction = get_direction,
|
||
|
.set_direction = set_direction,
|
||
|
.get_value = get_value,
|
||
|
.set_value = set_value,
|
||
|
.get_pull = get_pull,
|
||
|
.set_pull = set_pull,
|
||
|
};
|
||
|
|
||
|
void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
|
||
|
uintptr_t pinconf_base)
|
||
|
{
|
||
|
iproc_gpio.base = base;
|
||
|
iproc_gpio.nr_gpios = nr_gpios;
|
||
|
|
||
|
/* pinmux/pinconf base is optional for some SoCs */
|
||
|
if (pinmux_base)
|
||
|
iproc_gpio.pinmux_base = pinmux_base;
|
||
|
|
||
|
if (pinconf_base)
|
||
|
iproc_gpio.pinconf_base = pinconf_base;
|
||
|
|
||
|
gpio_init(&iproc_gpio_ops);
|
||
|
}
|