1 /*
2 * Copyright (c) 2024 Renesas Electronics Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT renesas_ra_external_interrupt
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/gpio/gpio_utils.h>
12 #include <zephyr/irq.h>
13 #include <zephyr/drivers/misc/renesas_ra_external_interrupt/renesas_ra_external_interrupt.h>
14 #include <soc.h>
15
16 enum ext_irq_trigger {
17 EXT_INTERRUPT_EDGE_FALLING = 0,
18 EXT_INTERRUPT_EDGE_RISING,
19 EXT_INTERRUPT_EDGE_BOTH,
20 EXT_INTERRUPT_EDGE_LOW_LEVEL,
21 };
22
23 enum ext_irq_sample_clock {
24 EXT_INTERRUPT_SAMPLE_CLOCK_DIV_1 = 0,
25 EXT_INTERRUPT_SAMPLE_CLOCK_DIV_8,
26 EXT_INTERRUPT_SAMPLE_CLOCK_DIV_32,
27 EXT_INTERRUPT_SAMPLE_CLOCK_DIV_64,
28 };
29
30 struct gpio_ra_irq_config {
31 mem_addr_t reg;
32 unsigned int channel;
33 enum ext_irq_trigger trigger;
34 enum ext_irq_sample_clock sample_clock;
35 bool digital_filter;
36 unsigned int irq;
37 };
38
39 struct gpio_ra_irq_data {
40 struct gpio_ra_callback callback;
41 struct k_sem irq_sem;
42 };
43
44 /**
45 * @brief setting interrupt for gpio input
46 *
47 * @param dev devive instance for gpio interrupt line
48 * @param callback setting context for the callback
49 * @retval 0 if success
50 * @retval -EBUSY if interrupt line is inuse
51 * @retval -ENOTSUP if interrupt mode is not supported
52 */
gpio_ra_interrupt_set(const struct device * dev,struct gpio_ra_callback * callback)53 int gpio_ra_interrupt_set(const struct device *dev, struct gpio_ra_callback *callback)
54 {
55 const struct gpio_ra_irq_config *config = dev->config;
56 struct gpio_ra_irq_data *data = dev->data;
57 uint8_t irqcr = sys_read8(config->reg) & ~R_ICU_IRQCR_IRQMD_Msk;
58
59 irq_disable(config->irq);
60
61 if (callback->mode == GPIO_INT_MODE_LEVEL) {
62 if (callback->trigger != GPIO_INT_TRIG_LOW) {
63 return -ENOTSUP;
64 }
65
66 irqcr |= (EXT_INTERRUPT_EDGE_LOW_LEVEL & R_ICU_IRQCR_IRQMD_Msk);
67 } else if (callback->mode == GPIO_INT_MODE_EDGE) {
68 switch (callback->trigger) {
69 case GPIO_INT_TRIG_LOW:
70 irqcr |= (EXT_INTERRUPT_EDGE_FALLING & R_ICU_IRQCR_IRQMD_Msk);
71 break;
72 case GPIO_INT_TRIG_HIGH:
73 irqcr |= (EXT_INTERRUPT_EDGE_RISING & R_ICU_IRQCR_IRQMD_Msk);
74 break;
75 case GPIO_INT_TRIG_BOTH:
76 irqcr |= (EXT_INTERRUPT_EDGE_BOTH & R_ICU_IRQCR_IRQMD_Msk);
77 break;
78 default:
79 return -ENOTSUP;
80 }
81 } else {
82 return -ENOTSUP;
83 }
84
85 if (data->callback.port_num != callback->port_num || data->callback.pin != callback->pin) {
86 if (0 != k_sem_take(&data->irq_sem, K_NO_WAIT)) {
87 return -EBUSY;
88 }
89 }
90
91 sys_write8(irqcr, config->reg);
92 data->callback = *callback;
93 irq_enable(config->irq);
94
95 return 0;
96 }
97
98 /**
99 * @brief unset interrupt configuration for the gpio interrupt
100 *
101 * @param dev device instance for port irq line
102 * @param port_num gpio port number
103 * @param pin the pin to disable interrupt
104 */
gpio_ra_interrupt_unset(const struct device * dev,uint8_t port_num,uint8_t pin)105 void gpio_ra_interrupt_unset(const struct device *dev, uint8_t port_num, uint8_t pin)
106 {
107 const struct gpio_ra_irq_config *config = dev->config;
108 struct gpio_ra_irq_data *data = dev->data;
109
110 if ((port_num != data->callback.port_num) && (pin != data->callback.pin)) {
111 return;
112 }
113
114 irq_disable(config->irq);
115 k_sem_give(&data->irq_sem);
116 }
117
gpio_ra_isr(const struct device * dev)118 void gpio_ra_isr(const struct device *dev)
119 {
120 const struct gpio_ra_irq_data *data = dev->data;
121 const struct gpio_ra_irq_config *config = dev->config;
122
123 data->callback.isr(data->callback.port, data->callback.pin);
124 R_BSP_IrqStatusClear(config->irq);
125 }
126
gpio_ra_interrupt_init(const struct device * dev)127 static int gpio_ra_interrupt_init(const struct device *dev)
128 {
129 const struct gpio_ra_irq_config *config = dev->config;
130 struct gpio_ra_irq_data *data = dev->data;
131 uint8_t irqcr = ((config->trigger << R_ICU_IRQCR_IRQMD_Pos));
132
133 WRITE_BIT(irqcr, R_ICU_IRQCR_FLTEN_Pos, config->digital_filter);
134 sys_write8(irqcr, config->reg);
135 k_sem_init(&data->irq_sem, 1, 1);
136
137 return 0;
138 }
139
140 #define GPIO_INTERRUPT_INIT(index) \
141 static const struct gpio_ra_irq_config gpio_ra_irq_config##index = { \
142 .reg = DT_INST_REG_ADDR(index), \
143 .channel = DT_INST_PROP(index, channel), \
144 .trigger = \
145 DT_INST_ENUM_IDX_OR(index, renesas_trigger, EXT_INTERRUPT_EDGE_FALLING), \
146 .digital_filter = DT_INST_PROP_OR(index, renesas_digital_filtering, false), \
147 .sample_clock = UTIL_CAT(EXT_INTERRUPT_SAMPLE_CLOCK_DIV_, \
148 DT_INST_PROP_OR(index, renesas_sample_clock_div, 1)), \
149 .irq = DT_INST_IRQ(index, irq), \
150 }; \
151 static struct gpio_ra_irq_data gpio_ra_irq_data##index; \
152 static int gpio_ra_irq_init##index(const struct device *dev) \
153 { \
154 R_ICU->IELSR[DT_INST_IRQ(index, irq)] = \
155 UTIL_CAT(ELC_EVENT_ICU_IRQ, DT_INST_PROP(index, channel)); \
156 IRQ_CONNECT(DT_INST_IRQ(index, irq), DT_INST_IRQ(index, priority), gpio_ra_isr, \
157 DEVICE_DT_INST_GET(index), 0); \
158 return gpio_ra_interrupt_init(dev); \
159 }; \
160 DEVICE_DT_INST_DEFINE(index, gpio_ra_irq_init##index, NULL, &gpio_ra_irq_data##index, \
161 &gpio_ra_irq_config##index, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
162 NULL);
163
164 DT_INST_FOREACH_STATUS_OKAY(GPIO_INTERRUPT_INIT)
165