1 /*
2  * Copyright 2020 Broadcom
3  * Copyright 2024 Meta
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT brcm_iproc_gpio
9 
10 #include <zephyr/arch/common/sys_bitops.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/drivers/gpio/gpio_utils.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/kernel.h>
16 
17 #define IPROC_GPIO_DATA_IN_OFFSET   0x00
18 #define IPROC_GPIO_DATA_OUT_OFFSET  0x04
19 #define IPROC_GPIO_OUT_EN_OFFSET    0x08
20 #define IPROC_GPIO_INT_TYPE_OFFSET  0x0c
21 #define IPROC_GPIO_INT_DE_OFFSET    0x10
22 #define IPROC_GPIO_INT_EDGE_OFFSET  0x14
23 #define IPROC_GPIO_INT_MSK_OFFSET   0x18
24 #define IPROC_GPIO_INT_STAT_OFFSET  0x1c
25 #define IPROC_GPIO_INT_MSTAT_OFFSET 0x20
26 #define IPROC_GPIO_INT_CLR_OFFSET   0x24
27 #define IPROC_GPIO_PAD_RES_OFFSET   0x34
28 #define IPROC_GPIO_RES_EN_OFFSET    0x38
29 
30 struct gpio_iproc_config {
31 	/* gpio_driver_config needs to be first */
32 	struct gpio_driver_config common;
33 	mem_addr_t base;
34 	void (*irq_config_func)(const struct device *dev);
35 };
36 
37 struct gpio_iproc_data {
38 	/* gpio_driver_data needs to be first */
39 	struct gpio_driver_data common;
40 	sys_slist_t cb;
41 };
42 
43 #define DEV_CFG(dev)  ((const struct gpio_iproc_config *const)(dev)->config)
44 #define DEV_DATA(dev) ((struct gpio_iproc_data *const)(dev)->data)
45 
gpio_iproc_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)46 static int gpio_iproc_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
47 {
48 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
49 	mem_addr_t base = cfg->base;
50 
51 	/* Setup the pin direcion. */
52 	if (flags & GPIO_OUTPUT) {
53 		/* configure pin for output */
54 		sys_set_bit(base + IPROC_GPIO_OUT_EN_OFFSET, pin);
55 	} else if (flags & GPIO_INPUT) {
56 		/* configure pin for input */
57 		sys_clear_bit(base + IPROC_GPIO_OUT_EN_OFFSET, pin);
58 	}
59 
60 	return 0;
61 }
62 
gpio_iproc_port_get_raw(const struct device * dev,uint32_t * value)63 static int gpio_iproc_port_get_raw(const struct device *dev, uint32_t *value)
64 {
65 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
66 	mem_addr_t base = cfg->base;
67 
68 	*value = sys_read32(base + IPROC_GPIO_DATA_IN_OFFSET);
69 
70 	return 0;
71 }
72 
gpio_iproc_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)73 static int gpio_iproc_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value)
74 {
75 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
76 	mem_addr_t base = cfg->base;
77 
78 	value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET);
79 	value = (value & (~mask)) | (value & mask);
80 	sys_write32(value, base + IPROC_GPIO_DATA_OUT_OFFSET);
81 
82 	return 0;
83 }
84 
gpio_iproc_port_set_bits_raw(const struct device * dev,uint32_t mask)85 static int gpio_iproc_port_set_bits_raw(const struct device *dev, uint32_t mask)
86 {
87 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
88 	mem_addr_t base = cfg->base;
89 
90 	sys_write32(mask, base + IPROC_GPIO_DATA_OUT_OFFSET);
91 
92 	return 0;
93 }
94 
gpio_iproc_port_clear_bits_raw(const struct device * dev,uint32_t mask)95 static int gpio_iproc_port_clear_bits_raw(const struct device *dev, uint32_t mask)
96 {
97 	uint32_t value;
98 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
99 	mem_addr_t base = cfg->base;
100 
101 	/* Clear pins. */
102 	value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET);
103 	value = (value & ~mask);
104 	sys_write32(value, base + IPROC_GPIO_DATA_OUT_OFFSET);
105 
106 	return 0;
107 }
108 
gpio_iproc_port_toggle_bits(const struct device * dev,uint32_t mask)109 static int gpio_iproc_port_toggle_bits(const struct device *dev, uint32_t mask)
110 {
111 	uint32_t value;
112 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
113 	mem_addr_t base = cfg->base;
114 
115 	/* toggles pins. */
116 	value = sys_read32(base + IPROC_GPIO_DATA_OUT_OFFSET);
117 	value = (value ^ mask);
118 	sys_write32(value, base + IPROC_GPIO_DATA_OUT_OFFSET);
119 
120 	return 0;
121 }
122 
gpio_iproc_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)123 static int gpio_iproc_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
124 					      enum gpio_int_mode mode, enum gpio_int_trig trig)
125 {
126 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
127 	mem_addr_t base = cfg->base;
128 
129 	/* check for interrupt configurations */
130 	if (mode & GPIO_INT_ENABLE) {
131 		if (mode & GPIO_INT_EDGE) {
132 			sys_clear_bit(base + IPROC_GPIO_INT_TYPE_OFFSET, pin);
133 		} else {
134 			sys_set_bit(base + IPROC_GPIO_INT_TYPE_OFFSET, pin);
135 		}
136 
137 		/* Generate interrupt of both falling/rising edge */
138 		if (trig & GPIO_INT_EDGE_BOTH) {
139 			sys_set_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin);
140 		} else if (trig & GPIO_INT_HIGH_1) {
141 			/* Generate interrupt on rising edge */
142 			sys_clear_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin);
143 			sys_set_bit(base + IPROC_GPIO_INT_EDGE_OFFSET, pin);
144 		} else if (trig & GPIO_INT_LOW_0) {
145 			/* Generate interrupt on falling edge */
146 			sys_clear_bit(base + IPROC_GPIO_INT_DE_OFFSET, pin);
147 			sys_clear_bit(base + IPROC_GPIO_INT_EDGE_OFFSET, pin);
148 		}
149 
150 		/* Unmask the interrupt */
151 		sys_clear_bit(base + IPROC_GPIO_INT_MSTAT_OFFSET, pin);
152 	} else {
153 		sys_set_bit(base + IPROC_GPIO_INT_MSK_OFFSET, pin);
154 	}
155 	return 0;
156 }
157 
gpio_iproc_isr(const struct device * dev)158 static void gpio_iproc_isr(const struct device *dev)
159 {
160 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
161 	mem_addr_t base = cfg->base;
162 	struct gpio_iproc_data *context = dev->data;
163 	uint32_t int_stat;
164 
165 	int_stat = sys_read32(base + IPROC_GPIO_INT_STAT_OFFSET);
166 
167 	/* Clear the source of the interrupt */
168 	sys_write32(int_stat, base + IPROC_GPIO_INT_CLR_OFFSET);
169 
170 	/* Handle the interrupt */
171 	gpio_fire_callbacks(&context->cb, dev, int_stat);
172 }
173 
gpio_iproc_manage_callback(const struct device * port,struct gpio_callback * callback,bool set)174 static int gpio_iproc_manage_callback(const struct device *port, struct gpio_callback *callback,
175 				      bool set)
176 {
177 	struct gpio_iproc_data *context = port->data;
178 
179 	return gpio_manage_callback(&context->cb, callback, set);
180 }
181 
182 static DEVICE_API(gpio, gpio_iproc_api) = {
183 	.pin_configure = gpio_iproc_configure,
184 	.port_get_raw = gpio_iproc_port_get_raw,
185 	.port_set_masked_raw = gpio_iproc_port_set_masked_raw,
186 	.port_set_bits_raw = gpio_iproc_port_set_bits_raw,
187 	.port_clear_bits_raw = gpio_iproc_port_clear_bits_raw,
188 	.port_toggle_bits = gpio_iproc_port_toggle_bits,
189 	.pin_interrupt_configure = gpio_iproc_pin_interrupt_configure,
190 	.manage_callback = gpio_iproc_manage_callback,
191 };
192 
gpio_iproc_init(const struct device * dev)193 int gpio_iproc_init(const struct device *dev)
194 {
195 	const struct gpio_iproc_config *const cfg = DEV_CFG(dev);
196 
197 	cfg->irq_config_func(dev);
198 
199 	return 0;
200 }
201 
202 #define GPIO_IPROC_INIT(n)                                                                         \
203 	static void port_iproc_config_func_##n(const struct device *dev)                           \
204 	{                                                                                          \
205 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), gpio_iproc_isr,             \
206 			    DEVICE_DT_INST_GET(n), 0);                                             \
207 		irq_enable(DT_INST_IRQN(n));                                                       \
208 	}                                                                                          \
209                                                                                                    \
210 	static const struct gpio_iproc_config gpio_port_config_##n = {                             \
211 		.common =                                                                          \
212 			{                                                                          \
213 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),               \
214 			},                                                                         \
215 		.base = DT_INST_REG_ADDR(n),                                                       \
216 		.irq_config_func = port_iproc_config_func_##n,                                     \
217 	};                                                                                         \
218                                                                                                    \
219 	static struct gpio_iproc_data gpio_port_data_##n;                                          \
220                                                                                                    \
221 	DEVICE_DT_INST_DEFINE(n, gpio_iproc_init, NULL, &gpio_port_data_##n,                       \
222 			      &gpio_port_config_##n, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY,       \
223 			      &gpio_iproc_api);
224 
225 DT_INST_FOREACH_STATUS_OKAY(GPIO_IPROC_INIT)
226