blob: 0081989a0499ee461cd133034f6324b53b973f1b [file] [log] [blame]
/*
* Copyright (c) 2022, Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_smartbond_gpio
#include <zephyr/drivers/gpio/gpio_utils.h>
#include <stdint.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/irq.h>
#include <zephyr/pm/device.h>
#include <DA1469xAB.h>
#include <da1469x_pdc.h>
#include <da1469x_pd.h>
#define GPIO_PUPD_INPUT 0
#define GPIO_PUPD_INPUT_PU 1
#define GPIO_PUPD_INPUT_PD 2
#define GPIO_PUPD_OUTPUT 3
/* GPIO P0 and P1 share single GPIO and WKUP peripheral instance with separate
* set registers for P0 and P1 interleaved. The starting registers for direct
* data access, bit access, mode, latch and wake-up controller are defined in
* device tree.
*/
struct gpio_smartbond_data_regs {
uint32_t data;
uint32_t _reserved0;
uint32_t set;
uint32_t _reserved1;
uint32_t reset;
};
struct gpio_smartbond_latch_regs {
uint32_t latch;
uint32_t set;
uint32_t reset;
};
struct gpio_smartbond_wkup_regs {
uint32_t select;
uint32_t _reserved0[4];
uint32_t pol;
uint32_t _reserved1[4];
uint32_t status;
uint32_t _reserved2[2];
uint32_t clear;
uint32_t _reserved3[2];
uint32_t sel;
};
struct gpio_smartbond_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
/* Pins that are configured for both edges (handled by software) */
gpio_port_pins_t both_edges_pins;
sys_slist_t callbacks;
#if CONFIG_PM_DEVICE
/*
* Saved state consist of:
* 1 word for GPIO output port state
* GPIOx_NGPIOS words for each pin mode
*/
uint32_t *gpio_saved_state;
#endif
};
struct gpio_smartbond_config {
/* gpio_driver_config needs to be first */
struct gpio_driver_config common;
volatile struct gpio_smartbond_data_regs *data_regs;
volatile uint32_t *mode_regs;
volatile struct gpio_smartbond_latch_regs *latch_regs;
volatile struct gpio_smartbond_wkup_regs *wkup_regs;
/* Value of TRIG_SELECT for PDC_CTRLx_REG entry */
uint8_t wkup_trig_select;
#if CONFIG_PM_DEVICE
uint8_t ngpios;
#endif
};
static void gpio_smartbond_wkup_init(void)
{
static bool wkup_init;
/* Wakeup controller is shared for both GPIO ports and should
* be initialized only once.
*/
if (!wkup_init) {
WAKEUP->WKUP_CTRL_REG = 0;
WAKEUP->WKUP_CLEAR_P0_REG = 0xffffffff;
WAKEUP->WKUP_CLEAR_P1_REG = 0xffffffff;
WAKEUP->WKUP_SELECT_P0_REG = 0;
WAKEUP->WKUP_SELECT_P1_REG = 0;
WAKEUP->WKUP_SEL_GPIO_P0_REG = 0;
WAKEUP->WKUP_SEL_GPIO_P1_REG = 0;
WAKEUP->WKUP_RESET_IRQ_REG = 0;
CRG_TOP->CLK_TMR_REG |= CRG_TOP_CLK_TMR_REG_WAKEUPCT_ENABLE_Msk;
WAKEUP->WKUP_CTRL_REG = WAKEUP_WKUP_CTRL_REG_WKUP_ENABLE_IRQ_Msk;
wkup_init = true;
}
}
static int gpio_smartbond_pin_configure(const struct device *dev,
gpio_pin_t pin, gpio_flags_t flags)
{
const struct gpio_smartbond_config *config = dev->config;
if (flags == GPIO_DISCONNECTED) {
/* Set pin as input with no resistors selected */
config->mode_regs[pin] = GPIO_PUPD_INPUT << GPIO_P0_00_MODE_REG_PUPD_Pos;
return 0;
}
if ((flags & GPIO_INPUT) && (flags & GPIO_OUTPUT)) {
/* Simultaneous in/out is not supported */
return -ENOTSUP;
}
if (flags & GPIO_OUTPUT) {
config->mode_regs[pin] = GPIO_PUPD_OUTPUT << GPIO_P0_00_MODE_REG_PUPD_Pos;
if (flags & GPIO_OUTPUT_INIT_LOW) {
config->data_regs->reset = BIT(pin);
} else if (flags & GPIO_OUTPUT_INIT_HIGH) {
config->data_regs->set = BIT(pin);
}
return 0;
}
if (flags & GPIO_PULL_DOWN) {
config->mode_regs[pin] = GPIO_PUPD_INPUT_PD << GPIO_P0_00_MODE_REG_PUPD_Pos;
} else if (flags & GPIO_PULL_UP) {
config->mode_regs[pin] = GPIO_PUPD_INPUT_PU << GPIO_P0_00_MODE_REG_PUPD_Pos;
} else {
config->mode_regs[pin] = GPIO_PUPD_INPUT << GPIO_P0_00_MODE_REG_PUPD_Pos;
}
return 0;
}
static int gpio_smartbond_port_get_raw(const struct device *dev,
gpio_port_value_t *value)
{
const struct gpio_smartbond_config *config = dev->config;
*value = config->data_regs->data;
return 0;
}
static int gpio_smartbond_port_set_masked_raw(const struct device *dev,
gpio_port_pins_t mask,
gpio_port_value_t value)
{
const struct gpio_smartbond_config *config = dev->config;
config->data_regs->set = value & mask;
config->data_regs->reset = ~value & mask;
return 0;
}
static int gpio_smartbond_port_set_bits_raw(const struct device *dev,
gpio_port_pins_t pins)
{
const struct gpio_smartbond_config *config = dev->config;
config->data_regs->set = pins;
return 0;
}
static int gpio_smartbond_port_clear_bits_raw(const struct device *dev,
gpio_port_pins_t pins)
{
const struct gpio_smartbond_config *config = dev->config;
config->data_regs->reset = pins;
return 0;
}
static int gpio_smartbond_port_toggle_bits(const struct device *dev,
gpio_port_pins_t mask)
{
const struct gpio_smartbond_config *config = dev->config;
volatile uint32_t *reg = &config->data_regs->data;
*reg = *reg ^ mask;
return 0;
}
static void gpio_smartbond_arm_next_edge_interrupt(const struct device *dev,
uint32_t pin_mask)
{
const struct gpio_smartbond_config *config = dev->config;
uint32_t pin_value;
do {
pin_value = config->data_regs->data & pin_mask;
if (pin_value) {
config->wkup_regs->pol |= pin_mask;
} else {
config->wkup_regs->pol &= ~pin_mask;
}
} while (pin_value != (config->data_regs->data & pin_mask));
}
static int gpio_smartbond_pin_interrupt_configure(const struct device *dev,
gpio_pin_t pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
const struct gpio_smartbond_config *config = dev->config;
struct gpio_smartbond_data *data = dev->data;
uint32_t pin_mask = BIT(pin);
#if CONFIG_PM
int trig_select_id = (config->wkup_trig_select << 5) | pin;
int pdc_ix;
#endif
/* Not supported by hardware */
if (mode == GPIO_INT_MODE_LEVEL) {
return -ENOTSUP;
}
#if CONFIG_PM
pdc_ix = da1469x_pdc_find(trig_select_id, MCU_PDC_MASTER_M33, MCU_PDC_EN_XTAL);
#endif
if (mode == GPIO_INT_MODE_DISABLED) {
config->wkup_regs->sel &= ~pin_mask;
config->wkup_regs->clear = pin_mask;
data->both_edges_pins &= ~pin_mask;
#if CONFIG_PM
if (pdc_ix >= 0) {
da1469x_pdc_del(pdc_ix);
}
#endif
} else {
if (trig == GPIO_INT_TRIG_BOTH) {
/* Not supported by hardware */
data->both_edges_pins |= pin_mask;
gpio_smartbond_arm_next_edge_interrupt(dev, pin_mask);
} else if (trig == GPIO_INT_TRIG_HIGH) {
config->wkup_regs->pol &= ~pin_mask;
} else {
config->wkup_regs->pol |= pin_mask;
}
config->wkup_regs->sel |= pin_mask;
#if CONFIG_PM
if (pdc_ix < 0) {
pdc_ix = da1469x_pdc_add(trig_select_id, MCU_PDC_MASTER_M33,
MCU_PDC_EN_XTAL);
}
if (pdc_ix < 0) {
return -ENOMEM;
}
#endif
}
return 0;
}
static int gpio_smartbond_manage_callback(const struct device *dev,
struct gpio_callback *callback, bool set)
{
struct gpio_smartbond_data *data = dev->data;
return gpio_manage_callback(&data->callbacks, callback, set);
}
static void gpio_smartbond_isr(const struct device *dev)
{
const struct gpio_smartbond_config *config = dev->config;
struct gpio_smartbond_data *data = dev->data;
uint32_t stat;
uint32_t two_edge_triggered;
WAKEUP->WKUP_RESET_IRQ_REG = WAKEUP_WKUP_RESET_IRQ_REG_WKUP_IRQ_RST_Msk;
stat = config->wkup_regs->status;
two_edge_triggered = stat & data->both_edges_pins;
while (two_edge_triggered) {
int pos = find_lsb_set(two_edge_triggered) - 1;
two_edge_triggered &= ~BIT(pos);
/* Re-arm for other edge */
gpio_smartbond_arm_next_edge_interrupt(dev, BIT(pos));
}
config->wkup_regs->clear = stat;
gpio_fire_callbacks(&data->callbacks, dev, stat);
}
#ifdef CONFIG_PM_DEVICE
static void gpio_latch_inst(mem_addr_t data_reg, mem_addr_t mode_reg, mem_addr_t latch_reg,
uint8_t ngpios, uint32_t *data, uint32_t *mode)
{
uint8_t idx;
*data = sys_read32(data_reg);
for (idx = 0; idx < ngpios; idx++, mode_reg += 4) {
mode[idx] = sys_read32(mode_reg);
}
sys_write32(BIT_MASK(ngpios), latch_reg);
}
static void gpio_unlatch_inst(mem_addr_t data_reg, mem_addr_t mode_reg, mem_addr_t latch_reg,
uint8_t ngpios, uint32_t data, uint32_t *mode)
{
uint8_t idx;
sys_write32(data, data_reg);
for (idx = 0; idx < ngpios; idx++, mode_reg += 4) {
sys_write32(mode[idx], mode_reg);
}
sys_write32(BIT_MASK(ngpios), latch_reg);
}
static void gpio_latch(const struct device *dev)
{
const struct gpio_smartbond_config *config = dev->config;
const struct gpio_smartbond_data *data = dev->data;
gpio_latch_inst((mem_addr_t)&config->data_regs->data,
(mem_addr_t)config->mode_regs,
(mem_addr_t)&config->latch_regs->reset,
config->ngpios, data->gpio_saved_state, data->gpio_saved_state + 1);
}
static void gpio_unlatch(const struct device *dev)
{
const struct gpio_smartbond_config *config = dev->config;
const struct gpio_smartbond_data *data = dev->data;
gpio_unlatch_inst((mem_addr_t)&config->data_regs->data,
(mem_addr_t)config->mode_regs,
(mem_addr_t)&config->latch_regs->set,
config->ngpios, data->gpio_saved_state[0], data->gpio_saved_state + 1);
}
static int gpio_smartbond_pm_action(const struct device *dev,
enum pm_device_action action)
{
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
da1469x_pd_acquire(MCU_PD_DOMAIN_COM);
gpio_unlatch(dev);
break;
case PM_DEVICE_ACTION_SUSPEND:
gpio_latch(dev);
da1469x_pd_release(MCU_PD_DOMAIN_COM);
break;
default:
ret = -ENOTSUP;
}
return ret;
}
#endif /* CONFIG_PM_DEVICE */
/* GPIO driver registration */
static const struct gpio_driver_api gpio_smartbond_drv_api_funcs = {
.pin_configure = gpio_smartbond_pin_configure,
.port_get_raw = gpio_smartbond_port_get_raw,
.port_set_masked_raw = gpio_smartbond_port_set_masked_raw,
.port_set_bits_raw = gpio_smartbond_port_set_bits_raw,
.port_clear_bits_raw = gpio_smartbond_port_clear_bits_raw,
.port_toggle_bits = gpio_smartbond_port_toggle_bits,
.pin_interrupt_configure = gpio_smartbond_pin_interrupt_configure,
.manage_callback = gpio_smartbond_manage_callback,
};
#define GPIO_SAVED_STATE(id) gpio_smartbond_saved_state_##id
#define GPIO_PM_DEVICE_CFG(fld, val) \
COND_CODE_1(CONFIG_PM_DEVICE, (fld = val,), ())
#define GPIO_PM_DEVICE_STATE(id, ngpios) \
COND_CODE_1(CONFIG_PM_DEVICE, (static uint32_t GPIO_SAVED_STATE(id)[1 + ngpios];), ())
#define GPIO_SMARTBOND_DEVICE(id) \
GPIO_PM_DEVICE_STATE(id, DT_INST_PROP(id, ngpios)) \
static const struct gpio_smartbond_config gpio_smartbond_config_##id = { \
.common = { \
.port_pin_mask = \
GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \
}, \
.data_regs = (volatile struct gpio_smartbond_data_regs *) \
DT_INST_REG_ADDR_BY_NAME(id, data), \
.mode_regs = (volatile uint32_t *)DT_INST_REG_ADDR_BY_NAME(id, mode), \
.latch_regs = (volatile struct gpio_smartbond_latch_regs *) \
DT_INST_REG_ADDR_BY_NAME(id, latch), \
.wkup_regs = (volatile struct gpio_smartbond_wkup_regs *) \
DT_INST_REG_ADDR_BY_NAME(id, wkup), \
.wkup_trig_select = id, \
GPIO_PM_DEVICE_CFG(.ngpios, DT_INST_PROP(id, ngpios)) \
}; \
\
static struct gpio_smartbond_data gpio_smartbond_data_##id = { \
GPIO_PM_DEVICE_CFG(.gpio_saved_state, GPIO_SAVED_STATE(id)) \
}; \
\
static int gpio_smartbond_init_##id(const struct device *dev) \
{ \
da1469x_pd_acquire(MCU_PD_DOMAIN_COM); \
gpio_smartbond_wkup_init(); \
IRQ_CONNECT(DT_INST_IRQN(id), \
DT_INST_IRQ(id, priority), \
gpio_smartbond_isr, \
DEVICE_DT_INST_GET(id), 0); \
irq_enable(DT_INST_IRQN(id)); \
return 0; \
} \
\
PM_DEVICE_DEFINE(id, gpio_smartbond_pm_action); \
DEVICE_DT_INST_DEFINE(id, gpio_smartbond_init_##id, \
PM_DEVICE_GET(id), \
&gpio_smartbond_data_##id, \
&gpio_smartbond_config_##id, \
PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, \
&gpio_smartbond_drv_api_funcs);
DT_INST_FOREACH_STATUS_OKAY(GPIO_SMARTBOND_DEVICE)