1 /*
2  * Copyright (c) 2018 Zilogic Systems.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_stellaris_gpio
8 
9 #include <errno.h>
10 #include <zephyr/arch/cpu.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/irq.h>
14 #include <soc.h>
15 #include <zephyr/sys/sys_io.h>
16 #include <zephyr/drivers/gpio/gpio_utils.h>
17 
18 typedef void (*config_func_t)(const struct device *dev);
19 
20 struct gpio_stellaris_config {
21 	/* gpio_driver_config needs to be first */
22 	struct gpio_driver_config common;
23 	uint32_t base;
24 	uint32_t port_map;
25 	config_func_t config_func;
26 };
27 
28 struct gpio_stellaris_runtime {
29 	/* gpio_driver_data needs to be first */
30 	struct gpio_driver_data common;
31 	sys_slist_t cb;
32 };
33 
34 #define GPIO_REG_ADDR(base, offset) (base + offset)
35 
36 #define GPIO_RW_ADDR(base, offset, p)			 \
37 	(GPIO_REG_ADDR(base, offset) | (1 << (p + 2)))
38 
39 #define GPIO_RW_MASK_ADDR(base, offset, mask)		 \
40 	(GPIO_REG_ADDR(base, offset) | (mask << 2))
41 
42 enum gpio_regs {
43 	GPIO_DATA_OFFSET = 0x000,
44 	GPIO_DIR_OFFSET = 0x400,
45 	GPIO_DEN_OFFSET = 0x51C,
46 	GPIO_IS_OFFSET = 0x404,
47 	GPIO_IBE_OFFSET = 0x408,
48 	GPIO_IEV_OFFSET = 0x40C,
49 	GPIO_IM_OFFSET = 0x410,
50 	GPIO_MIS_OFFSET = 0x418,
51 	GPIO_ICR_OFFSET = 0x41C,
52 };
53 
gpio_stellaris_isr(const struct device * dev)54 static void gpio_stellaris_isr(const struct device *dev)
55 {
56 	const struct gpio_stellaris_config * const cfg = dev->config;
57 	struct gpio_stellaris_runtime *context = dev->data;
58 	uint32_t base = cfg->base;
59 	uint32_t int_stat = sys_read32(GPIO_REG_ADDR(base, GPIO_MIS_OFFSET));
60 
61 	gpio_fire_callbacks(&context->cb, dev, int_stat);
62 
63 	sys_write32(int_stat, GPIO_REG_ADDR(base, GPIO_ICR_OFFSET));
64 }
65 
gpio_stellaris_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)66 static int gpio_stellaris_configure(const struct device *dev,
67 				    gpio_pin_t pin, gpio_flags_t flags)
68 {
69 	const struct gpio_stellaris_config *cfg = dev->config;
70 	uint32_t base = cfg->base;
71 	uint32_t port_map = cfg->port_map;
72 
73 	if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) {
74 		return -ENOTSUP;
75 	}
76 
77 	if ((flags & GPIO_SINGLE_ENDED) != 0) {
78 		return -ENOTSUP;
79 	}
80 
81 	/* Check for pin availability */
82 	if (!sys_test_bit((uint32_t)&port_map, pin)) {
83 		return -EINVAL;
84 	}
85 
86 	if ((flags & GPIO_OUTPUT) != 0) {
87 		mm_reg_t mask_addr;
88 
89 		mask_addr = GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, BIT(pin));
90 		if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
91 			sys_write32(BIT(pin), mask_addr);
92 		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
93 			sys_write32(0, mask_addr);
94 		}
95 		sys_set_bit(GPIO_REG_ADDR(base, GPIO_DIR_OFFSET), pin);
96 		/* Pin digital enable */
97 		sys_set_bit(GPIO_REG_ADDR(base, GPIO_DEN_OFFSET), pin);
98 	} else if ((flags & GPIO_INPUT) != 0) {
99 		sys_clear_bit(GPIO_REG_ADDR(base, GPIO_DIR_OFFSET), pin);
100 		/* Pin digital enable */
101 		sys_set_bit(GPIO_REG_ADDR(base, GPIO_DEN_OFFSET), pin);
102 	} else {
103 		/* Pin digital disable */
104 		sys_clear_bit(GPIO_REG_ADDR(base, GPIO_DEN_OFFSET), pin);
105 	}
106 
107 	return 0;
108 }
109 
gpio_stellaris_port_get_raw(const struct device * dev,uint32_t * value)110 static int gpio_stellaris_port_get_raw(const struct device *dev,
111 				       uint32_t *value)
112 {
113 	const struct gpio_stellaris_config *cfg = dev->config;
114 	uint32_t base = cfg->base;
115 
116 	*value = sys_read32(GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, 0xff));
117 
118 	return 0;
119 }
120 
gpio_stellaris_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)121 static int gpio_stellaris_port_set_masked_raw(const struct device *dev,
122 					      uint32_t mask,
123 					      uint32_t value)
124 {
125 	const struct gpio_stellaris_config *cfg = dev->config;
126 	uint32_t base = cfg->base;
127 
128 	sys_write32(value, GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, mask));
129 
130 	return 0;
131 }
132 
gpio_stellaris_port_set_bits_raw(const struct device * dev,uint32_t mask)133 static int gpio_stellaris_port_set_bits_raw(const struct device *dev,
134 					    uint32_t mask)
135 {
136 	const struct gpio_stellaris_config *cfg = dev->config;
137 	uint32_t base = cfg->base;
138 
139 	sys_write32(mask, GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, mask));
140 
141 	return 0;
142 }
143 
gpio_stellaris_port_clear_bits_raw(const struct device * dev,uint32_t mask)144 static int gpio_stellaris_port_clear_bits_raw(const struct device *dev,
145 					      uint32_t mask)
146 {
147 	const struct gpio_stellaris_config *cfg = dev->config;
148 	uint32_t base = cfg->base;
149 
150 	sys_write32(0, GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, mask));
151 
152 	return 0;
153 }
154 
gpio_stellaris_port_toggle_bits(const struct device * dev,uint32_t mask)155 static int gpio_stellaris_port_toggle_bits(const struct device *dev,
156 					   uint32_t mask)
157 {
158 	const struct gpio_stellaris_config *cfg = dev->config;
159 	uint32_t base = cfg->base;
160 	uint32_t value;
161 
162 	value = sys_read32(GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, 0xff));
163 	value ^= mask;
164 	sys_write32(value, GPIO_RW_MASK_ADDR(base, GPIO_DATA_OFFSET, 0xff));
165 
166 	return 0;
167 }
168 
gpio_stellaris_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)169 static int gpio_stellaris_pin_interrupt_configure(const struct device *dev,
170 						  gpio_pin_t pin,
171 						  enum gpio_int_mode mode,
172 						  enum gpio_int_trig trig)
173 {
174 	const struct gpio_stellaris_config *cfg = dev->config;
175 	uint32_t base = cfg->base;
176 
177 	/* Check if GPIO port needs interrupt support */
178 	if (mode == GPIO_INT_MODE_DISABLED) {
179 		/* Set the mask to disable the interrupt */
180 		sys_set_bit(GPIO_REG_ADDR(base, GPIO_IM_OFFSET), pin);
181 	} else {
182 		if (mode == GPIO_INT_MODE_EDGE) {
183 			sys_clear_bit(GPIO_REG_ADDR(base, GPIO_IS_OFFSET), pin);
184 		} else {
185 			sys_set_bit(GPIO_REG_ADDR(base, GPIO_IS_OFFSET), pin);
186 		}
187 
188 		if (trig == GPIO_INT_TRIG_BOTH) {
189 			sys_set_bit(GPIO_REG_ADDR(base, GPIO_IBE_OFFSET), pin);
190 		} else if (trig == GPIO_INT_TRIG_HIGH) {
191 			sys_set_bit(GPIO_REG_ADDR(base, GPIO_IEV_OFFSET), pin);
192 		} else {
193 			sys_clear_bit(GPIO_REG_ADDR(base,
194 						    GPIO_IEV_OFFSET), pin);
195 		}
196 		/* Clear the Mask to enable the interrupt */
197 		sys_clear_bit(GPIO_REG_ADDR(base, GPIO_IM_OFFSET), pin);
198 	}
199 
200 	return 0;
201 }
202 
gpio_stellaris_init(const struct device * dev)203 static int gpio_stellaris_init(const struct device *dev)
204 {
205 	const struct gpio_stellaris_config *cfg = dev->config;
206 
207 	cfg->config_func(dev);
208 	return 0;
209 }
210 
gpio_stellaris_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)211 static int gpio_stellaris_manage_callback(const struct device *dev,
212 					  struct gpio_callback *callback,
213 					  bool set)
214 {
215 	struct gpio_stellaris_runtime *context = dev->data;
216 
217 	gpio_manage_callback(&context->cb, callback, set);
218 
219 	return 0;
220 }
221 
222 static const struct gpio_driver_api gpio_stellaris_driver_api = {
223 	.pin_configure = gpio_stellaris_configure,
224 	.port_get_raw = gpio_stellaris_port_get_raw,
225 	.port_set_masked_raw = gpio_stellaris_port_set_masked_raw,
226 	.port_set_bits_raw = gpio_stellaris_port_set_bits_raw,
227 	.port_clear_bits_raw = gpio_stellaris_port_clear_bits_raw,
228 	.port_toggle_bits = gpio_stellaris_port_toggle_bits,
229 	.pin_interrupt_configure = gpio_stellaris_pin_interrupt_configure,
230 	.manage_callback = gpio_stellaris_manage_callback,
231 };
232 
233 #define STELLARIS_GPIO_DEVICE(n)							\
234 	static void port_## n ##_stellaris_config_func(const struct device *dev);		\
235 											\
236 	static struct gpio_stellaris_runtime port_## n ##_stellaris_runtime;		\
237 											\
238 	static const struct gpio_stellaris_config gpio_stellaris_port_## n ##_config = {\
239 		.common = {								\
240 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),		\
241 		},									\
242 		.base = DT_INST_REG_ADDR(n),			\
243 		.port_map = BIT_MASK(DT_INST_PROP(n, ngpios)),		\
244 		.config_func = port_## n ##_stellaris_config_func,			\
245 	};										\
246 											\
247 	DEVICE_DT_INST_DEFINE(n,							\
248 			    gpio_stellaris_init,					\
249 			    NULL,							\
250 			    &port_## n ##_stellaris_runtime,				\
251 			    &gpio_stellaris_port_## n ##_config,			\
252 			    POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY,			\
253 			    &gpio_stellaris_driver_api);				\
254 											\
255 	static void port_## n ##_stellaris_config_func(const struct device *dev)		\
256 	{										\
257 		IRQ_CONNECT(DT_INST_IRQN(n),			\
258 			    DT_INST_IRQ(n, priority),		\
259 			    gpio_stellaris_isr,						\
260 			    DEVICE_DT_INST_GET(n), 0);					\
261 											\
262 		irq_enable(DT_INST_IRQN(n));			\
263 	}
264 
265 DT_INST_FOREACH_STATUS_OKAY(STELLARIS_GPIO_DEVICE)
266