blob: 4d875e82ef88cbb10bccfcd09ae769bf56fc26d6 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright 2019 Broadcom.
*/
#include <assert.h>
#include <drivers/bcm_gpio.h>
#include <initcall.h>
#include <io.h>
#include <mm/core_memprot.h>
#include <platform_config.h>
#include <trace.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_INT_MSK_OFFSET 0x18
#define GPIO_BANK_SIZE 0x200
#define NGPIOS_PER_BANK 32
#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
#define IPROC_GPIO_REG(pin, reg) ((reg) + \
GPIO_BANK(pin) * GPIO_BANK_SIZE)
#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
#define GPIO_BANK_CNT 5
#define SEC_GPIO_SIZE 0x4
#define IPROC_GPIO_SEC_CFG_REG(pin) \
(((GPIO_BANK_CNT - 1) - GPIO_BANK(pin)) * SEC_GPIO_SIZE)
static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist);
struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin)
{
struct bcm_gpio_chip *gc = NULL;
SLIST_FOREACH(gc, &gclist, link)
if ((pin >= gc->gpio_base) &&
(pin < (gc->gpio_base + gc->ngpios)))
return gc;
return NULL;
}
static bool __maybe_unused gpio_is_range_overlap(unsigned int start,
unsigned int end)
{
struct bcm_gpio_chip *gc = NULL;
SLIST_FOREACH(gc, &gclist, link)
if ((start < (gc->gpio_base + gc->ngpios)) &&
(end > gc->gpio_base))
return true;
return false;
}
static void iproc_set_bit(unsigned int reg, unsigned int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
assert(gc);
io_setbits32(gc->base + offset, BIT(shift));
}
static void iproc_clr_bit(unsigned int reg, unsigned int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
assert(gc);
io_clrbits32(gc->base + offset, BIT(shift));
}
static void iproc_gpio_set(unsigned int gpio, enum gpio_level val)
{
if (val == GPIO_LEVEL_HIGH)
iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
else
iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio);
}
static enum gpio_level iproc_gpio_get(unsigned int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
assert(gc);
if (io_read32(gc->base + offset) & BIT(shift))
return GPIO_LEVEL_HIGH;
else
return GPIO_LEVEL_LOW;
}
static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir)
{
if (dir == GPIO_DIR_OUT)
iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
else
iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
}
static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
assert(gc);
if (io_read32(gc->base + offset) & BIT(shift))
return GPIO_DIR_OUT;
else
return GPIO_DIR_IN;
}
static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio);
assert(gc);
if (io_read32(gc->base + offset) & BIT(shift))
return GPIO_INTERRUPT_ENABLE;
else
return GPIO_INTERRUPT_DISABLE;
}
static void iproc_gpio_set_itr(unsigned int gpio,
enum gpio_interrupt ena_dis)
{
if (ena_dis == GPIO_INTERRUPT_ENABLE)
iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
else
iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio);
}
static const struct gpio_ops bcm_gpio_ops = {
.get_direction = iproc_gpio_get_dir,
.set_direction = iproc_gpio_set_dir,
.get_value = iproc_gpio_get,
.set_value = iproc_gpio_set,
.get_interrupt = iproc_gpio_get_itr,
.set_interrupt = iproc_gpio_set_itr,
};
KEEP_PAGER(bcm_gpio_ops);
void iproc_gpio_set_secure(int gpiopin)
{
vaddr_t regaddr = 0;
unsigned int shift = IPROC_GPIO_SHIFT(gpiopin);
vaddr_t baseaddr = (vaddr_t)phys_to_virt(CHIP_SECURE_GPIO_CONTROL0_BASE,
MEM_AREA_IO_SEC);
regaddr = baseaddr + IPROC_GPIO_SEC_CFG_REG(gpiopin);
io_clrbits32(regaddr, BIT(shift));
}
static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr,
unsigned int gpio_base, unsigned int ngpios)
{
assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios));
gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC);
gc->chip.ops = &bcm_gpio_ops;
gc->gpio_base = gpio_base;
gc->ngpios = ngpios;
SLIST_INSERT_HEAD(&gclist, gc, link);
DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios);
}
static TEE_Result bcm_gpio_init(void)
{
struct bcm_gpio_chip *gc = NULL;
#ifdef SECURE_GPIO_BASE0
gc = malloc(sizeof(*gc));
if (gc == NULL)
return TEE_ERROR_OUT_OF_MEMORY;
iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0);
#endif
#ifdef SECURE_GPIO_BASE1
gc = malloc(sizeof(*gc));
if (gc == NULL)
return TEE_ERROR_OUT_OF_MEMORY;
iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1);
#endif
return TEE_SUCCESS;
}
driver_init(bcm_gpio_init);