blob: 386bae12bfb1d31b172bc2368254239e753f6a68 [file] [log] [blame]
/*
* Copyright (c) 2024 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rz_gpio
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/irq.h>
#include "r_ioport.h"
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio/gpio_utils.h>
#include "gpio_renesas_rz.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(rz_gpio, CONFIG_GPIO_LOG_LEVEL);
#define LOG_DEV_ERR(dev, format, ...) LOG_ERR("%s:" #format, (dev)->name, ##__VA_ARGS__)
#define LOG_DEV_DBG(dev, format, ...) LOG_DBG("%s:" #format, (dev)->name, ##__VA_ARGS__)
struct gpio_rz_config {
struct gpio_driver_config common;
uint8_t ngpios;
uint8_t port_num;
bsp_io_port_t fsp_port;
const ioport_cfg_t *fsp_cfg;
const ioport_api_t *fsp_api;
const struct device *int_dev;
uint8_t tint_num[GPIO_RZ_MAX_TINT_NUM];
};
struct gpio_rz_data {
struct gpio_driver_data common;
sys_slist_t cb;
ioport_instance_ctrl_t *fsp_ctrl;
struct k_spinlock lock;
};
struct gpio_rz_tint_isr_data {
const struct device *gpio_dev;
gpio_pin_t pin;
};
struct gpio_rz_tint_data {
struct gpio_rz_tint_isr_data tint_data[GPIO_RZ_MAX_TINT_NUM];
uint32_t irq_set_edge;
};
struct gpio_rz_tint_config {
void (*gpio_int_init)(void);
};
static int gpio_rz_pin_config_get_raw(bsp_io_port_pin_t port_pin, uint32_t *flags);
#ifdef CONFIG_GPIO_GET_CONFIG
static int gpio_rz_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *flags)
{
const struct gpio_rz_config *config = dev->config;
bsp_io_port_pin_t port_pin = config->fsp_port | pin;
gpio_rz_pin_config_get_raw(port_pin, flags);
return 0;
}
#endif
/* Get previous pin's configuration, used by pin_configure/pin_interrupt_configure api */
static int gpio_rz_pin_config_get_raw(bsp_io_port_pin_t port_pin, uint32_t *flags)
{
bsp_io_port_t port = (port_pin >> 8U) & 0xFF;
gpio_pin_t pin = port_pin & 0xFF;
volatile uint8_t *p_p = GPIO_RZ_IOPORT_P_REG_BASE_GET;
volatile uint16_t *p_pm = GPIO_RZ_IOPORT_PM_REG_BASE_GET;
uint8_t adr_offset;
uint8_t p_value;
uint16_t pm_value;
adr_offset = (uint8_t)GPIO_RZ_REG_OFFSET(port, pin);
p_p = &p_p[adr_offset];
p_pm = &p_pm[adr_offset];
p_value = GPIO_RZ_P_VALUE_GET(*p_p, pin);
pm_value = GPIO_RZ_PM_VALUE_GET(*p_pm, pin);
if (p_value) {
*flags |= GPIO_OUTPUT_INIT_HIGH;
} else {
*flags |= GPIO_OUTPUT_INIT_LOW;
}
*flags |= ((pm_value << 16));
return 0;
}
static int gpio_rz_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
bsp_io_port_pin_t port_pin = config->fsp_port | pin;
uint32_t ioport_config_data = 0;
gpio_flags_t pre_flags;
fsp_err_t err;
gpio_rz_pin_config_get_raw(port_pin, &pre_flags);
if (!flags) {
/* Disconnect mode */
ioport_config_data = 0;
} else if (!(flags & GPIO_OPEN_DRAIN)) {
/* PM register */
ioport_config_data &= GPIO_RZ_PIN_CONFIGURE_INPUT_OUTPUT_RESET;
if (flags & GPIO_INPUT) {
if (flags & GPIO_OUTPUT) {
ioport_config_data |= IOPORT_CFG_PORT_DIRECTION_OUTPUT_INPUT;
} else {
ioport_config_data |= IOPORT_CFG_PORT_DIRECTION_INPUT;
}
} else if (flags & GPIO_OUTPUT) {
ioport_config_data &= GPIO_RZ_PIN_CONFIGURE_INPUT_OUTPUT_RESET;
ioport_config_data |= IOPORT_CFG_PORT_DIRECTION_OUTPUT;
}
/* P register */
if (!(flags & (GPIO_OUTPUT_INIT_HIGH | GPIO_OUTPUT_INIT_LOW))) {
flags |= pre_flags & (GPIO_OUTPUT_INIT_HIGH | GPIO_OUTPUT_INIT_LOW);
}
if (flags & GPIO_OUTPUT_INIT_HIGH) {
ioport_config_data |= IOPORT_CFG_PORT_OUTPUT_HIGH;
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
ioport_config_data &= ~(IOPORT_CFG_PORT_OUTPUT_HIGH);
}
/* PUPD register */
if (flags & GPIO_PULL_UP) {
ioport_config_data |= IOPORT_CFG_PULLUP_ENABLE;
} else if (flags & GPIO_PULL_DOWN) {
ioport_config_data |= IOPORT_CFG_PULLUP_ENABLE;
}
/* ISEL register */
if (flags & GPIO_INT_ENABLE) {
ioport_config_data |= GPIO_RZ_PIN_CONFIGURE_INT_ENABLE;
} else if (flags & GPIO_INT_DISABLE) {
ioport_config_data &= GPIO_RZ_PIN_CONFIGURE_INT_DISABLE;
}
/* Drive Ability register */
ioport_config_data |= GPIO_RZ_PIN_CONFIGURE_GET_DRIVE_ABILITY(flags);
/* Filter register, see in renesas-rz-gpio-ioport.h */
ioport_config_data |= GPIO_RZ_PIN_CONFIGURE_GET_FILTER(flags);
} else {
return -ENOTSUP;
}
err = config->fsp_api->pinCfg(data->fsp_ctrl, port_pin, ioport_config_data);
if (err != FSP_SUCCESS) {
return -EIO;
}
return 0;
}
static int gpio_rz_port_get_raw(const struct device *dev, gpio_port_value_t *value)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
fsp_err_t err;
ioport_size_t port_value;
err = config->fsp_api->portRead(data->fsp_ctrl, config->fsp_port, &port_value);
if (err != FSP_SUCCESS) {
return -EIO;
}
*value = (gpio_port_value_t)port_value;
return 0;
}
static int gpio_rz_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
gpio_port_value_t value)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
ioport_size_t port_mask = (ioport_size_t)mask;
ioport_size_t port_value = (ioport_size_t)value;
fsp_err_t err;
err = config->fsp_api->portWrite(data->fsp_ctrl, config->fsp_port, port_value, port_mask);
if (err != FSP_SUCCESS) {
return -EIO;
}
return 0;
}
static int gpio_rz_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
ioport_size_t mask = (ioport_size_t)pins;
ioport_size_t value = (ioport_size_t)pins;
fsp_err_t err;
err = config->fsp_api->portWrite(data->fsp_ctrl, config->fsp_port, value, mask);
if (err != FSP_SUCCESS) {
return -EIO;
}
return 0;
}
static int gpio_rz_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
ioport_size_t mask = (ioport_size_t)pins;
ioport_size_t value = 0x00;
fsp_err_t err;
err = config->fsp_api->portWrite(data->fsp_ctrl, config->fsp_port, value, mask);
if (err != FSP_SUCCESS) {
return -EIO;
}
return 0;
}
static int gpio_rz_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
bsp_io_port_pin_t port_pin;
gpio_flags_t pre_flags;
ioport_size_t value = 0;
fsp_err_t err;
for (uint8_t idx = 0; idx < config->ngpios; idx++) {
if (pins & (1U << idx)) {
port_pin = config->fsp_port | idx;
gpio_rz_pin_config_get_raw(port_pin, &pre_flags);
if (pre_flags & GPIO_OUTPUT_INIT_HIGH) {
value &= (1U << idx);
} else if (pre_flags & GPIO_OUTPUT_INIT_LOW) {
value |= (1U << idx);
}
}
}
err = config->fsp_api->portWrite(data->fsp_ctrl, config->fsp_port, value,
(ioport_size_t)pins);
if (err != FSP_SUCCESS) {
return -EIO;
}
return 0;
}
#define GPIO_RZ_HAS_INTERRUPT DT_HAS_COMPAT_STATUS_OKAY(renesas_rz_gpio_int)
#if GPIO_RZ_HAS_INTERRUPT
static int gpio_rz_int_disable(const struct device *dev, uint8_t tint_num)
{
struct gpio_rz_tint_data *data = dev->data;
volatile uint32_t *tssr = &R_INTC_IM33->TSSR0;
volatile uint32_t *titsr = &R_INTC_IM33->TITSR0;
volatile uint32_t *tscr = &R_INTC_IM33->TSCR;
/* Get register offset base on interrupt number. */
tssr = &tssr[tint_num / 4];
titsr = &titsr[tint_num / 16];
irq_disable(GPIO_RZ_TINT_IRQ_GET(tint_num));
/* Disable interrupt and clear interrupt source. */
*tssr &= ~(0xFF << GPIO_RZ_TSSR_OFFSET(tint_num));
/* Reset interrupt dectect type to default. */
*titsr &= ~(0x3 << GPIO_RZ_TITSR_OFFSET(tint_num));
/* Clear interrupt detection status. */
if (data->irq_set_edge & BIT(tint_num)) {
*tscr &= ~BIT(tint_num);
data->irq_set_edge &= ~BIT(tint_num);
}
data->tint_data[tint_num].gpio_dev = NULL;
data->tint_data[tint_num].pin = UINT8_MAX;
return 0;
}
static int gpio_rz_int_enable(const struct device *int_dev, const struct device *gpio_dev,
uint8_t tint_num, uint8_t irq_type, gpio_pin_t pin)
{
struct gpio_rz_tint_data *int_data = int_dev->data;
const struct gpio_rz_config *gpio_config = gpio_dev->config;
volatile uint32_t *tssr = &R_INTC_IM33->TSSR0;
volatile uint32_t *titsr = &R_INTC_IM33->TITSR0;
tssr = &tssr[tint_num / 4];
titsr = &titsr[tint_num / 16];
/* Select interrupt detect type. */
*titsr |= (irq_type << GPIO_RZ_TITSR_OFFSET(tint_num));
/* Select interrupt source base on port and pin number.*/
*tssr |= (GPIO_RZ_TSSR_VAL(gpio_config->port_num, pin)) << GPIO_RZ_TSSR_OFFSET(tint_num);
if (irq_type == GPIO_RZ_TINT_EDGE_RISING || irq_type == GPIO_RZ_TINT_EDGE_FALLING) {
int_data->irq_set_edge |= BIT(tint_num);
/* Clear interrupt status. */
R_INTC_IM33->TSCR &= ~BIT(tint_num);
}
int_data->tint_data[tint_num].gpio_dev = gpio_dev;
int_data->tint_data[tint_num].pin = pin;
irq_enable(GPIO_RZ_TINT_IRQ_GET(tint_num));
return 0;
}
static int gpio_rz_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
enum gpio_int_mode mode, enum gpio_int_trig trig)
{
const struct gpio_rz_config *config = dev->config;
struct gpio_rz_data *data = dev->data;
bsp_io_port_pin_t port_pin = config->fsp_port | pin;
uint8_t tint_num = config->tint_num[pin];
uint8_t irq_type = 0;
gpio_flags_t pre_flags = 0;
k_spinlock_key_t key;
if (tint_num >= GPIO_RZ_MAX_TINT_NUM) {
LOG_DEV_ERR(dev, "Invalid TINT interrupt:%d >= %d", tint_num, GPIO_RZ_MAX_TINT_NUM);
}
if (pin > config->ngpios) {
return -EINVAL;
}
if (trig == GPIO_INT_TRIG_BOTH) {
return -ENOTSUP;
}
key = k_spin_lock(&data->lock);
if (mode == GPIO_INT_MODE_DISABLED) {
gpio_rz_pin_config_get_raw(port_pin, &pre_flags);
pre_flags |= GPIO_INT_DISABLE;
gpio_rz_pin_configure(dev, pin, pre_flags);
gpio_rz_int_disable(config->int_dev, tint_num);
goto exit_unlock;
}
if (mode == GPIO_INT_MODE_EDGE) {
irq_type = GPIO_RZ_TINT_EDGE_RISING;
if (trig == GPIO_INT_TRIG_LOW) {
irq_type = GPIO_RZ_TINT_EDGE_FALLING;
}
} else {
irq_type = GPIO_RZ_TINT_LEVEL_HIGH;
if (trig == GPIO_INT_TRIG_LOW) {
irq_type = GPIO_RZ_TINT_LEVEL_LOW;
}
}
/* Set register ISEL */
gpio_rz_pin_config_get_raw(port_pin, &pre_flags);
pre_flags |= GPIO_INT_ENABLE;
gpio_rz_pin_configure(dev, pin, pre_flags);
gpio_rz_int_enable(config->int_dev, dev, tint_num, irq_type, pin);
exit_unlock:
k_spin_unlock(&data->lock, key);
return 0;
}
static int gpio_rz_manage_callback(const struct device *dev, struct gpio_callback *callback,
bool set)
{
struct gpio_rz_data *data = dev->data;
return gpio_manage_callback(&data->cb, callback, set);
}
static void gpio_rz_isr(const struct device *dev, uint8_t pin)
{
struct gpio_rz_data *data = dev->data;
gpio_fire_callbacks(&data->cb, dev, BIT(pin));
}
static void gpio_rz_tint_isr(uint16_t irq, const struct device *dev)
{
struct gpio_rz_tint_data *data = dev->data;
volatile uint32_t *tscr = &R_INTC_IM33->TSCR;
uint8_t tint_num;
tint_num = irq - GPIO_RZ_TINT_IRQ_OFFSET;
if (!(*tscr & BIT(tint_num))) {
LOG_DEV_DBG(dev, "tint:%u spurious irq, status 0", tint_num);
return;
}
if (data->irq_set_edge & BIT(tint_num)) {
*tscr &= ~BIT(tint_num);
}
gpio_rz_isr(data->tint_data[tint_num].gpio_dev, data->tint_data[tint_num].pin);
}
static int gpio_rz_int_init(const struct device *dev)
{
const struct gpio_rz_tint_config *config = dev->config;
config->gpio_int_init();
return 0;
}
#endif
static DEVICE_API(gpio, gpio_rz_driver_api) = {
.pin_configure = gpio_rz_pin_configure,
#ifdef CONFIG_GPIO_GET_CONFIG
.pin_get_config = gpio_rz_pin_get_config,
#endif
.port_get_raw = gpio_rz_port_get_raw,
.port_set_masked_raw = gpio_rz_port_set_masked_raw,
.port_set_bits_raw = gpio_rz_port_set_bits_raw,
.port_clear_bits_raw = gpio_rz_port_clear_bits_raw,
.port_toggle_bits = gpio_rz_port_toggle_bits,
#if GPIO_RZ_HAS_INTERRUPT
.pin_interrupt_configure = gpio_rz_pin_interrupt_configure,
.manage_callback = gpio_rz_manage_callback,
#endif
};
/*Initialize GPIO interrupt device*/
#define GPIO_RZ_TINT_ISR_DECLARE(irq_num, node_id) \
static void rz_gpio_isr_##irq_num(void *param) \
{ \
gpio_rz_tint_isr(DT_IRQ_BY_IDX(node_id, irq_num, irq), param); \
}
#define GPIO_RZ_TINT_ISR_INIT(node_id, irq_num) LISTIFY(irq_num, \
GPIO_RZ_TINT_ISR_DECLARE, (), node_id)
#define GPIO_RZ_TINT_CONNECT(irq_num, node_id) \
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, irq_num, irq), \
DT_IRQ_BY_IDX(node_id, irq_num, priority), rz_gpio_isr_##irq_num, \
DEVICE_DT_GET(node_id), 0);
#define GPIO_RZ_TINT_CONNECT_FUNC(node_id) \
static void rz_gpio_tint_connect_func##node_id(void) \
{ \
LISTIFY(DT_NUM_IRQS(node_id), \
GPIO_RZ_TINT_CONNECT, (;), \
node_id) \
}
/* Initialize GPIO device*/
#define GPIO_RZ_INT_INIT(node_id) \
GPIO_RZ_TINT_ISR_INIT(node_id, DT_NUM_IRQS(node_id)) \
GPIO_RZ_TINT_CONNECT_FUNC(node_id) \
static const struct gpio_rz_tint_config rz_gpio_tint_cfg_##node_id = { \
.gpio_int_init = rz_gpio_tint_connect_func##node_id, \
}; \
static struct gpio_rz_tint_data rz_gpio_tint_data_##node_id = {}; \
DEVICE_DT_DEFINE(node_id, gpio_rz_int_init, NULL, &rz_gpio_tint_data_##node_id, \
&rz_gpio_tint_cfg_##node_id, POST_KERNEL, \
UTIL_DEC(CONFIG_GPIO_INIT_PRIORITY), NULL);
DT_FOREACH_STATUS_OKAY(renesas_rz_gpio_int, GPIO_RZ_INT_INIT)
#define VALUE_2X(i, _) UTIL_X2(i)
#define PIN_IRQ_GET(idx, inst) \
COND_CODE_1(DT_INST_PROP_HAS_IDX(inst, irqs, idx), \
([DT_INST_PROP_BY_IDX(inst, irqs, idx)] = \
DT_INST_PROP_BY_IDX(inst, irqs, UTIL_INC(idx)),), \
())
#define PIN_IRQS_GET(inst) \
FOR_EACH_FIXED_ARG(PIN_IRQ_GET, (), inst, \
LISTIFY(DT_INST_PROP_LEN_OR(inst, irqs, 0), VALUE_2X, (,)))
#define RZG_GPIO_PORT_INIT(inst) \
static ioport_cfg_t g_ioport_##inst##_cfg = { \
.number_of_pins = 0, \
.p_pin_cfg_data = NULL, \
.p_extend = NULL, \
}; \
static const struct gpio_rz_config gpio_rz_##inst##_config = { \
.common = \
{ \
.port_pin_mask = \
(gpio_port_pins_t)GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
}, \
.fsp_port = (uint32_t)DT_INST_REG_ADDR(inst), \
.port_num = (uint8_t)DT_NODE_CHILD_IDX(DT_DRV_INST(inst)), \
.ngpios = (uint8_t)DT_INST_PROP(inst, ngpios), \
.fsp_cfg = &g_ioport_##inst##_cfg, \
.fsp_api = &g_ioport_on_ioport, \
.int_dev = DEVICE_DT_GET_OR_NULL(DT_INST(0, renesas_rz_gpio_int)), \
.tint_num = {PIN_IRQS_GET(inst)}, \
}; \
static ioport_instance_ctrl_t g_ioport_##inst##_ctrl; \
static struct gpio_rz_data gpio_rz_##inst##_data = { \
.fsp_ctrl = &g_ioport_##inst##_ctrl, \
}; \
DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &gpio_rz_##inst##_data, &gpio_rz_##inst##_config, \
POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, &gpio_rz_driver_api);
DT_INST_FOREACH_STATUS_OKAY(RZG_GPIO_PORT_INIT)