drivers: gpio: add interrupt support to renesas rzt2m gpio
Add interrupt support to renesas rzt2m gpio driver Signed-off-by: Jakub Michalski <jmichalski@internships.antmicro.com>
This commit is contained in:
parent
311aa332c8
commit
e92c04cbeb
|
@ -9,15 +9,27 @@
|
|||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/drivers/syscon.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/errno_private.h>
|
||||
#include <zephyr/dt-bindings/gpio/renesas-rzt2m-gpio.h>
|
||||
#include <soc.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/irq.h>
|
||||
|
||||
static const struct device *const ns_portnf_md_dev = DEVICE_DT_GET(DT_NODELABEL(ns_portnf_md));
|
||||
|
||||
#define PMm_OFFSET 0x200
|
||||
#define PMCm_OFFSET 0x400
|
||||
#define PFCm_OFFSET 0x600
|
||||
#define PINm_OFFSET 0x800
|
||||
#define DRCTLm_OFFSET 0xa00
|
||||
|
||||
#define PMm_SIZE 0x2
|
||||
#define DRCTLm_SIZE 0x8
|
||||
#define PFCm_SIZE 0x4
|
||||
|
||||
/* config defines in include/zephyr/dt-bindings/gpio/renesas-rzt2m-gpio.h */
|
||||
#define DRIVE_SHIFT 0
|
||||
#define SCHMITT_TRIGGER_SHIFT 4
|
||||
#define SLEW_RATE_SHIFT 5
|
||||
|
@ -27,8 +39,21 @@
|
|||
#define PULL_UP (1 << PULL_SHIFT)
|
||||
#define PULL_DOWN (2 << PULL_SHIFT)
|
||||
|
||||
#define INT_INVERT 0
|
||||
#define INT_FALLING_EDGE 1
|
||||
#define INT_RISING_EDGE 2
|
||||
#define INT_BOTH_EDGE 3
|
||||
|
||||
#define IRQ_COUNT 16
|
||||
#define NS_IRQ_COUNT 14
|
||||
|
||||
#define MAX_PORT_SIZE 8
|
||||
|
||||
#define RZT2M_GPIO_VALUE_IDENTITY(i, _) i
|
||||
|
||||
struct rzt2m_gpio_config {
|
||||
struct gpio_driver_config common;
|
||||
uint8_t pin_irqs[MAX_PORT_SIZE];
|
||||
uint8_t *port_nsr;
|
||||
uint8_t *ptadr;
|
||||
uint8_t port;
|
||||
|
@ -36,17 +61,29 @@ struct rzt2m_gpio_config {
|
|||
|
||||
struct rzt2m_gpio_data {
|
||||
struct gpio_driver_data common;
|
||||
sys_slist_t cb;
|
||||
};
|
||||
|
||||
struct rzt2m_gpio_irq_slot {
|
||||
const struct device *dev;
|
||||
uint8_t pin;
|
||||
};
|
||||
|
||||
struct rzt2m_gpio_common_data {
|
||||
struct rzt2m_gpio_irq_slot irq_registered_ports[IRQ_COUNT];
|
||||
};
|
||||
|
||||
static struct rzt2m_gpio_common_data rzt2m_gpio_common_data_inst;
|
||||
|
||||
static void rzt2m_gpio_unlock(void)
|
||||
{
|
||||
rzt2m_unlock_prcrn(PRCRN_PRC1);
|
||||
rzt2m_unlock_prcrn(PRCRN_PRC1 | PRCRN_PRC2);
|
||||
rzt2m_unlock_prcrs(PRCRS_GPIO);
|
||||
}
|
||||
|
||||
static void rzt2m_gpio_lock(void)
|
||||
{
|
||||
rzt2m_lock_prcrn(PRCRN_PRC1);
|
||||
rzt2m_lock_prcrn(PRCRN_PRC1 | PRCRN_PRC2);
|
||||
rzt2m_lock_prcrs(PRCRS_GPIO);
|
||||
}
|
||||
|
||||
|
@ -71,7 +108,7 @@ static volatile uint16_t *rzt2m_gpio_get_pm_reg(const struct device *dev)
|
|||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
|
||||
return (volatile uint16_t *)(config->port_nsr + PMm_OFFSET + 0x2 * config->port);
|
||||
return (volatile uint16_t *)(config->port_nsr + PMm_OFFSET + PMm_SIZE * config->port);
|
||||
}
|
||||
|
||||
/* IO Buffer m function switching register */
|
||||
|
@ -79,7 +116,7 @@ static volatile uint64_t *rzt2m_gpio_get_drctl_reg(const struct device *dev)
|
|||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
|
||||
return (volatile uint64_t *)(config->port_nsr + DRCTLm_OFFSET + 0x8 * config->port);
|
||||
return (volatile uint64_t *)(config->port_nsr + DRCTLm_OFFSET + DRCTLm_SIZE * config->port);
|
||||
}
|
||||
|
||||
/* Port m region select register */
|
||||
|
@ -90,6 +127,22 @@ static volatile uint8_t *rzt2m_gpio_get_rselp_reg(const struct device *dev)
|
|||
return (volatile uint8_t *)(config->ptadr + config->port);
|
||||
}
|
||||
|
||||
/* Port m mode control register */
|
||||
static volatile uint8_t *rzt2m_gpio_get_pmc_reg(const struct device *dev, uint8_t port)
|
||||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
|
||||
return (volatile uint8_t *)(config->port_nsr + PMCm_OFFSET + port);
|
||||
}
|
||||
|
||||
/* Port m function control register */
|
||||
static volatile uint32_t *rzt2m_gpio_get_pfc_reg(const struct device *dev, uint8_t port)
|
||||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
|
||||
return (volatile uint32_t *)(config->port_nsr + PFCm_OFFSET + PFCm_SIZE * port);
|
||||
}
|
||||
|
||||
static int rzt2m_gpio_init(const struct device *dev)
|
||||
{
|
||||
rzt2m_gpio_unlock();
|
||||
|
@ -211,22 +264,193 @@ static int rzt2m_gpio_configure(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rzt2m_gpio_get_pin_irq(const struct device *dev, gpio_pin_t pin)
|
||||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
|
||||
if (pin >= MAX_PORT_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
return config->pin_irqs[pin] - 1;
|
||||
}
|
||||
|
||||
static bool rzt2m_gpio_is_irq_used_by_other_pin(const struct device *dev, gpio_pin_t pin,
|
||||
uint8_t irq)
|
||||
{
|
||||
if (irq >= IRQ_COUNT) {
|
||||
return false;
|
||||
}
|
||||
if (rzt2m_gpio_common_data_inst.irq_registered_ports[irq].dev == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (rzt2m_gpio_common_data_inst.irq_registered_ports[irq].dev != dev) {
|
||||
return true;
|
||||
}
|
||||
return rzt2m_gpio_common_data_inst.irq_registered_ports[irq].pin != pin;
|
||||
}
|
||||
|
||||
static void rzt2m_gpio_isr(uint8_t *irq_n)
|
||||
{
|
||||
const struct device *dev = rzt2m_gpio_common_data_inst.irq_registered_ports[*irq_n].dev;
|
||||
|
||||
if (dev) {
|
||||
struct rzt2m_gpio_data *data = dev->data;
|
||||
int irq_pin = rzt2m_gpio_common_data_inst.irq_registered_ports[*irq_n].pin;
|
||||
|
||||
if (irq_pin >= 0) {
|
||||
gpio_fire_callbacks(&data->cb, dev, 1 << irq_pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rzt2m_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
|
||||
enum gpio_int_mode mode, enum gpio_int_trig trig)
|
||||
{
|
||||
const struct rzt2m_gpio_config *config = dev->config;
|
||||
volatile uint8_t *pmc_reg = rzt2m_gpio_get_pmc_reg(dev, config->port);
|
||||
volatile uint32_t *pfc_reg = rzt2m_gpio_get_pfc_reg(dev, config->port);
|
||||
uint32_t ns_portnf_md_val = 0;
|
||||
|
||||
syscon_read_reg(ns_portnf_md_dev, 0, &ns_portnf_md_val);
|
||||
|
||||
/* level interrupts are not supported */
|
||||
if (mode == GPIO_INT_MODE_LEVEL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint8_t irq = rzt2m_gpio_get_pin_irq(dev, pin);
|
||||
bool irq_used_by_other = rzt2m_gpio_is_irq_used_by_other_pin(dev, pin, irq);
|
||||
|
||||
if (irq < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* secure range - currently not supported*/
|
||||
if (irq >= NS_IRQ_COUNT) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (mode == GPIO_INT_MODE_DISABLED) {
|
||||
rzt2m_gpio_unlock();
|
||||
WRITE_BIT(*pmc_reg, pin, 0);
|
||||
|
||||
/* check if selected pin is using irq line to avoid unregistering other pin irq
|
||||
* handler
|
||||
*/
|
||||
if (!irq_used_by_other) {
|
||||
rzt2m_gpio_common_data_inst.irq_registered_ports[irq].dev = NULL;
|
||||
}
|
||||
rzt2m_gpio_lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the irq line is used by another pin */
|
||||
if (irq_used_by_other) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
uint8_t md_mode = 0x0;
|
||||
|
||||
switch (trig) {
|
||||
case GPIO_INT_TRIG_LOW:
|
||||
md_mode = INT_FALLING_EDGE;
|
||||
break;
|
||||
case GPIO_INT_TRIG_HIGH:
|
||||
md_mode = INT_RISING_EDGE;
|
||||
break;
|
||||
case GPIO_INT_TRIG_BOTH:
|
||||
md_mode = INT_BOTH_EDGE;
|
||||
}
|
||||
|
||||
rzt2m_gpio_unlock();
|
||||
|
||||
uint32_t mdx_mask =
|
||||
~((uint32_t)0b11 << irq); /* description of interrupt type has length of 2 bits */
|
||||
ns_portnf_md_val = (ns_portnf_md_val & mdx_mask) | (md_mode << irq);
|
||||
syscon_write_reg(ns_portnf_md_dev, 0, ns_portnf_md_val); /* set interrupt type */
|
||||
|
||||
WRITE_BIT(*pmc_reg, pin, 1); /* enable special function on selected pin */
|
||||
|
||||
/* in case of every pin on every port irq function number is 0 */
|
||||
*pfc_reg &= ~((uint32_t)0b1111 << pin * 4);
|
||||
|
||||
/* register handling interrupt in isr for selected port and pin */
|
||||
rzt2m_gpio_common_data_inst.irq_registered_ports[irq].dev = dev;
|
||||
rzt2m_gpio_common_data_inst.irq_registered_ports[irq].pin = pin;
|
||||
|
||||
rzt2m_gpio_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzt2m_gpio_manage_callback(const struct device *dev, struct gpio_callback *cb, bool set)
|
||||
{
|
||||
struct rzt2m_gpio_data *data = dev->data;
|
||||
|
||||
return gpio_manage_callback(&data->cb, cb, set);
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api rzt2m_gpio_driver_api = {
|
||||
.pin_configure = rzt2m_gpio_configure,
|
||||
.port_get_raw = rzt2m_gpio_get_raw,
|
||||
.port_set_masked_raw = rzt2m_port_set_masked_raw,
|
||||
.port_set_bits_raw = rzt2m_port_set_bits_raw,
|
||||
.port_clear_bits_raw = rzt2m_port_clear_bits_raw,
|
||||
.port_toggle_bits = rzt2m_gpio_toggle};
|
||||
.port_toggle_bits = rzt2m_gpio_toggle,
|
||||
.pin_interrupt_configure = rzt2m_gpio_pin_interrupt_configure,
|
||||
.manage_callback = rzt2m_gpio_manage_callback};
|
||||
|
||||
#define RZT2M_INIT_IRQ(irq_n) \
|
||||
IRQ_CONNECT(DT_IRQ_BY_IDX(DT_INST(0, renesas_rzt2m_gpio_common), irq_n, irq), \
|
||||
DT_IRQ_BY_IDX(DT_INST(0, renesas_rzt2m_gpio_common), irq_n, priority), \
|
||||
rzt2m_gpio_isr, &n[irq_n], \
|
||||
DT_IRQ_BY_IDX(DT_INST(0, renesas_rzt2m_gpio_common), irq_n, flags)) \
|
||||
irq_enable(DT_IRQ_BY_IDX(DT_INST(0, renesas_rzt2m_gpio_common), irq_n, irq));
|
||||
|
||||
static int rzt2m_gpio_common_init(const struct device *dev)
|
||||
{
|
||||
struct rzt2m_gpio_common_data *data = dev->data;
|
||||
|
||||
static uint8_t n[IRQ_COUNT];
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(n); i++) {
|
||||
n[i] = i;
|
||||
data->irq_registered_ports[i].dev = NULL;
|
||||
}
|
||||
|
||||
FOR_EACH(RZT2M_INIT_IRQ, (), LISTIFY(NS_IRQ_COUNT, RZT2M_GPIO_VALUE_IDENTITY, (,)))
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEVICE_DT_DEFINE(DT_INST(0, renesas_rzt2m_gpio_common),
|
||||
rzt2m_gpio_common_init,
|
||||
NULL,
|
||||
&rzt2m_gpio_common_data_inst, NULL,
|
||||
PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY,
|
||||
NULL);
|
||||
|
||||
#define VALUE_2X(i, _) UTIL_X2(i)
|
||||
|
||||
#define PIN_IRQ_INITIALIZER(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)) + 1,), \
|
||||
())
|
||||
|
||||
#define PORT_IRQS_INITIALIZER(inst) \
|
||||
FOR_EACH_FIXED_ARG(PIN_IRQ_INITIALIZER, (), inst, \
|
||||
LISTIFY(DT_INST_PROP_LEN_OR(inst, irqs, 0), VALUE_2X, (,)))
|
||||
|
||||
#define RZT2M_GPIO_DEFINE(inst) \
|
||||
static struct rzt2m_gpio_data rzt2m_gpio_data##inst; \
|
||||
static struct rzt2m_gpio_config rzt2m_gpio_config##inst = { \
|
||||
.port_nsr = (uint8_t *)DT_REG_ADDR_BY_NAME(DT_INST_PARENT(inst), port_nsr), \
|
||||
.ptadr = (uint8_t *)DT_REG_ADDR_BY_NAME(DT_INST_PARENT(inst), ptadr), \
|
||||
.port_nsr = (uint8_t *)DT_REG_ADDR_BY_NAME(DT_INST_GPARENT(inst), port_nsr), \
|
||||
.ptadr = (uint8_t *)DT_REG_ADDR_BY_NAME(DT_INST_GPARENT(inst), ptadr), \
|
||||
.port = DT_INST_REG_ADDR(inst), \
|
||||
.pin_irqs = {PORT_IRQS_INITIALIZER(inst)}, \
|
||||
.common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst)}}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, rzt2m_gpio_init, NULL, &rzt2m_gpio_data##inst, \
|
||||
DEVICE_DT_INST_DEFINE(inst, rzt2m_gpio_init, NULL, &rzt2m_gpio_data##inst, \
|
||||
&rzt2m_gpio_config##inst, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
|
||||
&rzt2m_gpio_driver_api);
|
||||
|
||||
|
|
Loading…
Reference in a new issue