blob: 4e29a572674f17681c07292abd23702881f7cdf8 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <soc/gpio.h>
#include <device/mmio.h>
#include <endian.h>
#include <soc/addressmap.h>
union gpio_const {
u64 u;
struct {
u64 gpios:8; /** Number of GPIOs implemented */
u64 pp:8; /** Number of PP vectors */
u64:48; /* Reserved */
} s;
};
union bit_cfg {
u64 u;
struct {
u64 tx_oe : 1; /* Output Enable */
u64 xor : 1; /* Invert */
u64 int_en : 1; /* Interrupt Enable */
u64 int_type : 1; /* Type of Interrupt */
u64 filt_cnt : 4; /* Glitch filter counter */
u64 filt_sel : 4; /* Glitch filter select */
u64 tx_od : 1; /* Set Output to Open Drain */
u64 : 3;
u64 pin_sel : 10; /* Select type of pin */
u64 : 38;
} s;
};
struct cavium_gpio {
u64 rx_dat;
u64 tx_set;
u64 tx_clr;
u64 multicast;
u64 ocla_exten_trg;
u64 strap;
u64 reserved[12];
union gpio_const gpio_const; /* Offset 90 */
u64 reserved2[109];
union bit_cfg bit_cfg[48]; /* Offset 400 */
};
/* Base address of GPIO BAR */
static const void *gpio_get_baseaddr(void)
{
return (const void *)GPIO_PF_BAR0;
}
/* Number of GPIO pins. Usually 48. */
gpio_t gpio_pin_count(void)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union gpio_const gpio_const;
gpio_const.u = read64(&regs->gpio_const.u);
if (gpio_const.s.gpios > 64)
return 64; // FIXME: Add support for more than 64 GPIOs
return gpio_const.s.gpios;
}
/* Set GPIO to software control and direction INPUT */
void gpio_input(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
printk(BIOS_SPEW, "GPIO(%u): direction input\n", gpio);
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.pin_sel = 0;
bit_cfg.s.tx_oe = 0;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
}
/* Set GPIO of direction OUTPUT to level */
void gpio_set(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return;
printk(BIOS_SPEW, "GPIO(%u): level: %u\n", gpio, !!value);
if (value)
write64(&regs->tx_set, 1ULL << gpio);
else
write64(&regs->tx_clr, 1ULL << gpio);
}
/* Set GPIO direction to OUTPUT with level */
void gpio_output(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
gpio_set(gpio, value);
printk(BIOS_SPEW, "GPIO(%u): direction output with level: %u\n", gpio,
!!value);
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.pin_sel = 0;
bit_cfg.s.tx_oe = 1;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
}
/* Set GPIO invert flag, that affects INPUT and OUTPUT */
void gpio_invert(gpio_t gpio, int value)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
union bit_cfg bit_cfg;
if (gpio >= gpio_pin_count())
return;
bit_cfg.u = read64(&regs->bit_cfg[gpio]);
bit_cfg.s.xor = !!value;
write64(&regs->bit_cfg[gpio], bit_cfg.u);
printk(BIOS_SPEW, "GPIO(%u): invert: %s\n", gpio, value ? "ON" : "OFF");
}
/* Read GPIO level with direction set to INPUT */
int gpio_get(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return 0;
const u64 reg = read64(&regs->rx_dat);
printk(BIOS_SPEW, "GPIO(%u): input: %u\n", gpio,
!!(reg & (1ULL << gpio)));
return !!(reg & (1ULL << gpio));
}
/* Read GPIO STRAP level sampled at cold boot */
int gpio_strap_value(gpio_t gpio)
{
struct cavium_gpio *regs = (struct cavium_gpio *)gpio_get_baseaddr();
if (gpio >= gpio_pin_count())
return 0;
const u64 reg = read64(&regs->strap);
printk(BIOS_SPEW, "GPIO(%u): strap: %u\n", gpio,
!!(reg & (1ULL << gpio)));
return !!(reg & (1ULL << gpio));
}
/* FIXME: Parse devicetree ? */
void gpio_init(void)
{
const size_t pin_count = gpio_pin_count();
printk(BIOS_DEBUG, "GPIO: base address: %p, pin count: %zd\n",
gpio_get_baseaddr(), pin_count);
if (!pin_count)
return;
}
void gpio_input_pulldown(gpio_t gpio)
{
}
void gpio_input_pullup(gpio_t gpio)
{
}