1 /*
2  * Copyright (c) 2022 Schlumberger
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT infineon_xmc4xxx_intc
8 
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/dt-bindings/interrupt-controller/infineon-xmc4xxx-intc.h>
13 #include <zephyr/irq.h>
14 
15 #include <xmc_eru.h>
16 
17 /* In Infineon XMC4XXX SoCs, gpio interrupts are triggered via an Event Request Unit (ERU) */
18 /* module. A subset of the GPIOs are connected to the ERU. The ERU monitors edge triggers */
19 /* and creates a SR. */
20 
21 /* This driver configures the ERU for a target port/pin combination for rising/falling */
22 /* edge events. Note that the ERU module does not generate SR based on the gpio level. */
23 /* Internally the ERU tracks the *status* of an event. The status is set on a positive edge and */
24 /* unset on a negative edge (or vice-versa depending on the configuration). The value of */
25 /* the status is used to implement a level triggered interrupt; The ISR checks the status */
26 /* flag and calls the callback function if the status is set. */
27 
28 /* The ERU configurations for supported port/pin combinations are stored in a devicetree file */
29 /* dts/arm/infineon/xmc4xxx_x_x-intc.dtsi. The configurations are stored in the opaque array */
30 /* uint16 port_line_mapping[]. The bitfields for the opaque entries are defined in */
31 /* dt-bindings/interrupt-controller/infineon-xmc4xxx-intc.h. */
32 
33 struct isr_cb {
34 	/* if fn is NULL it implies the interrupt line has not been allocated */
35 	void (*fn)(const struct device *dev, int pin);
36 	void *data;
37 	enum gpio_int_mode mode;
38 	uint8_t port_id;
39 	uint8_t pin;
40 };
41 
42 #define MAX_ISR_NUM 8
43 struct intc_xmc4xxx_data {
44 	struct isr_cb cb[MAX_ISR_NUM];
45 };
46 
47 #define NUM_ERUS 2
48 struct intc_xmc4xxx_config {
49 	XMC_ERU_t *eru_regs[NUM_ERUS];
50 };
51 
52 static const uint16_t port_line_mapping[DT_INST_PROP_LEN(0, port_line_mapping)] =
53 				DT_INST_PROP(0, port_line_mapping);
54 
intc_xmc4xxx_gpio_enable_interrupt(int port_id,int pin,enum gpio_int_mode mode,enum gpio_int_trig trig,void (* fn)(const struct device *,int),void * user_data)55 int intc_xmc4xxx_gpio_enable_interrupt(int port_id, int pin, enum gpio_int_mode mode,
56 				       enum gpio_int_trig trig,
57 				       void (*fn)(const struct device *, int), void *user_data)
58 {
59 	const struct device *dev = DEVICE_DT_INST_GET(0);
60 	struct intc_xmc4xxx_data *data = dev->data;
61 	const struct intc_xmc4xxx_config *config = dev->config;
62 	int ret = -ENOTSUP;
63 
64 	for (int i = 0; i < ARRAY_SIZE(port_line_mapping); i++) {
65 		XMC_ERU_ETL_CONFIG_t etl_config = {0};
66 		XMC_ERU_OGU_CONFIG_t isr_config = {0};
67 		XMC_ERU_ETL_EDGE_DETECTION_t trig_xmc;
68 		XMC_ERU_t *eru;
69 		int port_map, pin_map, line, eru_src, eru_ch;
70 		struct isr_cb *cb;
71 
72 		port_map = XMC4XXX_INTC_GET_PORT(port_line_mapping[i]);
73 		pin_map  = XMC4XXX_INTC_GET_PIN(port_line_mapping[i]);
74 
75 		if (port_map != port_id || pin_map != pin) {
76 			continue;
77 		}
78 
79 		line = XMC4XXX_INTC_GET_LINE(port_line_mapping[i]);
80 		cb = &data->cb[line];
81 		if (cb->fn) {
82 			/* It's already used. Continue search for available line */
83 			/* with same port/pin */
84 			ret = -EBUSY;
85 			continue;
86 		}
87 
88 		eru_src = XMC4XXX_INTC_GET_ERU_SRC(port_line_mapping[i]);
89 		eru_ch  = line & 0x3;
90 
91 		if (trig == GPIO_INT_TRIG_HIGH) {
92 			trig_xmc = XMC_ERU_ETL_EDGE_DETECTION_RISING;
93 		} else if (trig == GPIO_INT_TRIG_LOW) {
94 			trig_xmc = XMC_ERU_ETL_EDGE_DETECTION_FALLING;
95 		} else if (trig == GPIO_INT_TRIG_BOTH) {
96 			trig_xmc = XMC_ERU_ETL_EDGE_DETECTION_BOTH;
97 		} else {
98 			return -EINVAL;
99 		}
100 
101 		cb->port_id = port_id;
102 		cb->pin = pin;
103 		cb->mode = mode;
104 		cb->fn = fn;
105 		cb->data = user_data;
106 
107 		/* setup the eru */
108 		etl_config.edge_detection = trig_xmc;
109 		etl_config.input_a = eru_src;
110 		etl_config.input_b = eru_src;
111 		etl_config.source = eru_src >> 2;
112 		etl_config.status_flag_mode = XMC_ERU_ETL_STATUS_FLAG_MODE_HWCTRL;
113 		etl_config.enable_output_trigger = 1;
114 		etl_config.output_trigger_channel = eru_ch;
115 
116 		eru = config->eru_regs[line >> 2];
117 
118 		XMC_ERU_ETL_Init(eru, eru_ch, &etl_config);
119 
120 		isr_config.service_request = XMC_ERU_OGU_SERVICE_REQUEST_ON_TRIGGER;
121 		XMC_ERU_OGU_Init(eru, eru_ch, &isr_config);
122 
123 		/* if the gpio level is already set then we must manually set the interrupt to */
124 		/* pending */
125 		if (mode == GPIO_INT_MODE_LEVEL) {
126 			ret = gpio_pin_get_raw(user_data, pin);
127 			if (ret < 0) {
128 				return ret;
129 			}
130 #define NVIC_ISPR_BASE 0xe000e200u
131 			if ((ret == 0 && trig == GPIO_INT_TRIG_LOW) ||
132 			    (ret == 1 && trig == GPIO_INT_TRIG_HIGH)) {
133 				eru->EXICON_b[eru_ch].FL = 1;
134 				/* put interrupt into pending state */
135 				*(uint32_t *)(NVIC_ISPR_BASE) |= BIT(line + 1);
136 			}
137 		}
138 
139 		return 0;
140 	}
141 	return ret;
142 }
143 
intc_xmc4xxx_gpio_disable_interrupt(int port_id,int pin)144 int intc_xmc4xxx_gpio_disable_interrupt(int port_id, int pin)
145 {
146 	const struct device *dev = DEVICE_DT_INST_GET(0);
147 	const struct intc_xmc4xxx_config *config = dev->config;
148 	struct intc_xmc4xxx_data *data = dev->data;
149 	int eru_ch;
150 
151 	for (int line = 0; line < ARRAY_SIZE(data->cb); line++) {
152 		struct isr_cb *cb;
153 
154 		cb = &data->cb[line];
155 		eru_ch = line & 0x3;
156 		if (cb->fn && cb->port_id == port_id && cb->pin == pin) {
157 			XMC_ERU_t *eru = config->eru_regs[line >> 2];
158 
159 			cb->fn = NULL;
160 			/* disable the SR */
161 			eru->EXICON_b[eru_ch].PE = 0;
162 			/* unset the status flag */
163 			eru->EXICON_b[eru_ch].FL = 0;
164 			/* no need to clear other variables in cb*/
165 			return 0;
166 		}
167 	}
168 	return -EINVAL;
169 }
170 
intc_xmc4xxx_isr(void * arg)171 static void intc_xmc4xxx_isr(void *arg)
172 {
173 	int line = (int)arg;
174 	const struct device *dev = DEVICE_DT_INST_GET(0);
175 	struct intc_xmc4xxx_data *data = dev->data;
176 	const struct intc_xmc4xxx_config *config = dev->config;
177 	struct isr_cb *cb = &data->cb[line];
178 	XMC_ERU_t *eru = config->eru_regs[line >> 2];
179 	int eru_ch = line & 0x3;
180 
181 	/* The callback function may actually disable the interrupt and set cb->fn = NULL */
182 	/* as is done in tests/drivers/gpio/gpio_api_1pin. Assume that the callback function */
183 	/* will NOT disable the interrupt and then enable another port/pin */
184 	/* in the same callback which could potentially set cb->fn again. */
185 	while (cb->fn) {
186 		cb->fn(cb->data, cb->pin);
187 		/* for level triggered interrupts we have to manually check the status. */
188 		if (cb->mode == GPIO_INT_MODE_LEVEL && eru->EXICON_b[eru_ch].FL == 1) {
189 			continue;
190 		}
191 		/* break for edge triggered interrupts */
192 		break;
193 	}
194 }
195 
196 #define INTC_IRQ_CONNECT_ENABLE(name, line_number)                                                \
197 	COND_CODE_1(DT_INST_IRQ_HAS_NAME(0, name),                                                \
198 	(IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, name, irq),                                           \
199 		DT_INST_IRQ_BY_NAME(0, name, priority), intc_xmc4xxx_isr, (void *)line_number, 0); \
200 		irq_enable(DT_INST_IRQ_BY_NAME(0, name, irq));), ())
201 
intc_xmc4xxx_init(const struct device * dev)202 static int intc_xmc4xxx_init(const struct device *dev)
203 {
204 	/* connect irqs only if they defined by name in the dts */
205 	INTC_IRQ_CONNECT_ENABLE(eru0sr0, 0);
206 	INTC_IRQ_CONNECT_ENABLE(eru0sr1, 1);
207 	INTC_IRQ_CONNECT_ENABLE(eru0sr2, 2);
208 	INTC_IRQ_CONNECT_ENABLE(eru0sr3, 3);
209 	INTC_IRQ_CONNECT_ENABLE(eru1sr0, 4);
210 	INTC_IRQ_CONNECT_ENABLE(eru1sr1, 5);
211 	INTC_IRQ_CONNECT_ENABLE(eru1sr2, 6);
212 	INTC_IRQ_CONNECT_ENABLE(eru1sr3, 7);
213 	return 0;
214 }
215 
216 struct intc_xmc4xxx_data intc_xmc4xxx_data0;
217 
218 struct intc_xmc4xxx_config intc_xmc4xxx_config0 = {
219 	.eru_regs = {
220 		(XMC_ERU_t *)DT_INST_REG_ADDR_BY_NAME(0, eru0),
221 		(XMC_ERU_t *)DT_INST_REG_ADDR_BY_NAME(0, eru1),
222 	},
223 };
224 
225 DEVICE_DT_INST_DEFINE(0, intc_xmc4xxx_init, NULL,
226 		&intc_xmc4xxx_data0, &intc_xmc4xxx_config0, PRE_KERNEL_1,
227 		CONFIG_INTC_INIT_PRIORITY, NULL);
228