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