1 /*
2  * Copyright (c) 2024 GARDENA GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT silabs_si32_gpio
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/gpio/gpio_utils.h>
12 #include <zephyr/kernel.h>
13 
14 #include <SI32_PBCFG_A_Type.h>
15 #include <SI32_PBSTD_A_Type.h>
16 #include <si32_device.h>
17 
18 #include <errno.h>
19 
20 struct gpio_si32_config {
21 	struct gpio_driver_config common;
22 	SI32_PBSTD_A_Type *base;
23 	bool disable_pullups;
24 };
25 
26 struct gpio_si32_data {
27 	/* gpio_driver_data needs to be first */
28 	struct gpio_driver_data common;
29 	sys_slist_t cb;
30 	gpio_port_pins_t trig_low;
31 	gpio_port_pins_t trig_high;
32 	uint32_t pin_values;
33 };
34 
gpio_si32_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)35 static int gpio_si32_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
36 {
37 	const struct gpio_si32_config *config = dev->config;
38 	uint32_t key = irq_lock();
39 
40 	/* Simultaneous input & output mode not supported */
41 	if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
42 		return -ENOTSUP;
43 	}
44 
45 	if (flags & GPIO_OUTPUT) {
46 		if (flags & GPIO_OUTPUT_INIT_HIGH) {
47 			SI32_PBSTD_A_write_pins_high(config->base, BIT(pin));
48 		} else if (flags & GPIO_OUTPUT_INIT_LOW) {
49 			SI32_PBSTD_A_write_pins_low(config->base, BIT(pin));
50 		}
51 
52 		SI32_PBSTD_A_set_pins_push_pull_output(config->base, BIT(pin));
53 	} else if (flags & GPIO_INPUT) {
54 		SI32_PBSTD_A_set_pins_digital_input(config->base, BIT(pin));
55 	} else {
56 		SI32_PBSTD_A_set_pins_analog(config->base, BIT(pin));
57 	}
58 
59 	/* Initially, configure interrupt to trigger on the active value.
60 	 * Otherwise we'd get an interrupt immediately after enabling them.
61 	 */
62 	if (flags & GPIO_ACTIVE_HIGH) {
63 		config->base->PM_SET = BIT(pin);
64 	} else {
65 		config->base->PM_CLR = BIT(pin);
66 	}
67 
68 	irq_unlock(key);
69 
70 	return 0;
71 }
72 
gpio_si32_port_get_raw(const struct device * dev,uint32_t * value)73 static int gpio_si32_port_get_raw(const struct device *dev, uint32_t *value)
74 {
75 	const struct gpio_si32_config *config = dev->config;
76 
77 	*value = SI32_PBSTD_A_read_pins(config->base);
78 
79 	return 0;
80 }
81 
gpio_si32_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)82 static int gpio_si32_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
83 					 gpio_port_value_t value)
84 {
85 	const struct gpio_si32_config *config = dev->config;
86 
87 	SI32_PBSTD_A_write_pins_masked(config->base, value, mask);
88 
89 	return 0;
90 }
91 
gpio_si32_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)92 static int gpio_si32_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
93 {
94 	const struct gpio_si32_config *config = dev->config;
95 
96 	SI32_PBSTD_A_write_pins_high(config->base, pins);
97 
98 	return 0;
99 }
100 
gpio_si32_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)101 static int gpio_si32_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
102 {
103 	const struct gpio_si32_config *config = dev->config;
104 
105 	SI32_PBSTD_A_write_pins_low(config->base, pins);
106 
107 	return 0;
108 }
109 
gpio_si32_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)110 static int gpio_si32_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
111 {
112 	const struct gpio_si32_config *config = dev->config;
113 
114 	SI32_PBSTD_A_toggle_pins(config->base, pins);
115 
116 	return 0;
117 }
118 
gpio_si32_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)119 static int gpio_si32_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
120 					     enum gpio_int_mode mode, enum gpio_int_trig trig)
121 {
122 	const struct gpio_si32_config *config = dev->config;
123 	struct gpio_si32_data *data = dev->data;
124 	int ret;
125 	const uint32_t key = irq_lock();
126 
127 	if (mode == GPIO_INT_MODE_DISABLED) {
128 		config->base->PMEN_CLR = BIT(pin);
129 		WRITE_BIT(data->trig_low, pin, 0);
130 		WRITE_BIT(data->trig_high, pin, 0);
131 
132 		ret = 0;
133 		goto unlock;
134 	} else if (mode == GPIO_INT_MODE_EDGE) {
135 	} else {
136 		/* Not yet implemented */
137 		ret = -ENOTSUP;
138 		goto unlock;
139 	}
140 
141 	WRITE_BIT(data->trig_low, pin, trig & GPIO_INT_TRIG_LOW);
142 	WRITE_BIT(data->trig_high, pin, trig & GPIO_INT_TRIG_HIGH);
143 
144 	config->base->PMEN_SET = BIT(pin);
145 
146 	ret = 0;
147 
148 unlock:
149 	irq_unlock(key);
150 
151 	return ret;
152 }
153 
gpio_si32_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)154 static int gpio_si32_manage_callback(const struct device *dev, struct gpio_callback *callback,
155 				     bool set)
156 {
157 	struct gpio_si32_data *data = dev->data;
158 
159 	return gpio_manage_callback(&data->cb, callback, set);
160 }
161 
162 static DEVICE_API(gpio, gpio_si32_driver) = {
163 	.pin_configure = gpio_si32_configure,
164 	.port_get_raw = gpio_si32_port_get_raw,
165 	.port_set_masked_raw = gpio_si32_port_set_masked_raw,
166 	.port_set_bits_raw = gpio_si32_port_set_bits_raw,
167 	.port_clear_bits_raw = gpio_si32_port_clear_bits_raw,
168 	.port_toggle_bits = gpio_si32_port_toggle_bits,
169 	.pin_interrupt_configure = gpio_si32_pin_interrupt_configure,
170 	.manage_callback = gpio_si32_manage_callback,
171 };
172 
gpio_si32_init(const struct device * dev)173 static int gpio_si32_init(const struct device *dev)
174 {
175 	const struct gpio_si32_config *config = dev->config;
176 
177 	if (config->disable_pullups) {
178 		SI32_PBSTD_A_disable_pullup_resistors(config->base);
179 	}
180 
181 	return 0;
182 }
183 
184 #define GPIO_DEVICE_INIT(inst)                                                                     \
185 	static const struct gpio_si32_config gpio_si32_cfg_##inst = {                              \
186 		.common =                                                                          \
187 			{                                                                          \
188 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_NGPIOS(16U),              \
189 			},                                                                         \
190 		.base = (SI32_PBSTD_A_Type *)DT_INST_REG_ADDR(inst),                               \
191 		.disable_pullups = DT_INST_PROP(inst, disable_pullups),                            \
192 	};                                                                                         \
193 	static struct gpio_si32_data gpio_si32_data_##inst;                                        \
194 	DEVICE_DT_INST_DEFINE(inst, gpio_si32_init, NULL, &gpio_si32_data_##inst,                  \
195 			      &gpio_si32_cfg_##inst, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY,      \
196 			      &gpio_si32_driver);
197 
198 DT_INST_FOREACH_STATUS_OKAY(GPIO_DEVICE_INIT)
199 
200 #define GPIO_DEVICE_LIST_ENTRY(inst) DEVICE_DT_GET(DT_DRV_INST(inst)),
201 static const struct device *gpio_devices[] = {DT_INST_FOREACH_STATUS_OKAY(GPIO_DEVICE_LIST_ENTRY)};
202 
203 /* The hardware only supports level interrupts, so we have to emulate edge
204  * interrupts in this handler.
205  */
gpio_si32_irq_handler(const struct device * arg)206 static void gpio_si32_irq_handler(const struct device *arg)
207 {
208 	ARG_UNUSED(arg);
209 
210 	irq_disable(PMATCH_IRQn);
211 	NVIC_ClearPendingIRQ(PMATCH_IRQn);
212 
213 	for (size_t i = 0; i < ARRAY_SIZE(gpio_devices); i++) {
214 		const struct device *dev = gpio_devices[i];
215 		const struct gpio_si32_config *config = dev->config;
216 		struct gpio_si32_data *data = dev->data;
217 		const uint32_t pmen = SI32_PBSTD_A_read_pmen(config->base);
218 		const uint32_t pm = SI32_PBSTD_A_read_pm(config->base);
219 		const uint32_t values = SI32_PBSTD_A_read_pins(config->base);
220 
221 		/* Invert triggers for all pins which are at their trigger
222 		 * values. This disables interrupts until they change again
223 		 * since the hardware only supports level interrupts.
224 		 */
225 		const uint32_t pins_not_at_trigger_value = (pm ^ values) & pmen;
226 		const uint32_t pins_at_trigger_value = (~pins_not_at_trigger_value) & pmen;
227 
228 		SI32_PBSTD_A_write_pm(config->base, pm ^ pins_at_trigger_value);
229 
230 		/* To check which pins actually changed we have to store and
231 		 * compare the previous value.
232 		 */
233 		const uint32_t changed_pins = (values ^ data->pin_values) & pmen;
234 
235 		data->pin_values = values;
236 
237 		if (changed_pins) {
238 			/* The user might be interested in both levels or just one,
239 			 * so filter those events here.
240 			 */
241 			const uint32_t changed_pins_high =
242 				(values & changed_pins) & data->trig_high;
243 			const uint32_t changed_pins_low =
244 				((~values) & changed_pins) & data->trig_low;
245 
246 			gpio_fire_callbacks(&data->cb, dev, changed_pins_high | changed_pins_low);
247 		}
248 	}
249 
250 	irq_enable(PMATCH_IRQn);
251 }
252 
gpio_si32_common_init(void)253 static int gpio_si32_common_init(void)
254 {
255 	/* This is the only mode we support right now */
256 	SI32_PBCFG_A_select_port_match_mode_pin_match(SI32_PBCFG_0);
257 
258 	IRQ_CONNECT(PMATCH_IRQn, 0, gpio_si32_irq_handler, NULL, 0);
259 	irq_enable(PMATCH_IRQn);
260 
261 	return 0;
262 }
263 SYS_INIT(gpio_si32_common_init, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY);
264