1 /*
2 * Copyright (c) 2016 Linaro Limited.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT arm_cmsdk_gpio
8
9 #include <zephyr/kernel.h>
10
11 #include <zephyr/device.h>
12 #include <errno.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/init.h>
15 #include <soc.h>
16 #include <zephyr/drivers/clock_control/arm_clock_control.h>
17 #include <zephyr/drivers/gpio/gpio_cmsdk_ahb.h>
18 #include <zephyr/irq.h>
19
20 #include <zephyr/drivers/gpio/gpio_utils.h>
21
22 /**
23 * @brief GPIO driver for ARM CMSDK AHB GPIO
24 */
25
26 typedef void (*gpio_config_func_t)(const struct device *port);
27
28 struct gpio_cmsdk_ahb_cfg {
29 /* gpio_driver_config needs to be first */
30 struct gpio_driver_config common;
31 volatile struct gpio_cmsdk_ahb *port;
32 gpio_config_func_t gpio_config_func;
33 /* GPIO Clock control in Active State */
34 struct arm_clock_control_t gpio_cc_as;
35 /* GPIO Clock control in Sleep State */
36 struct arm_clock_control_t gpio_cc_ss;
37 /* GPIO Clock control in Deep Sleep State */
38 struct arm_clock_control_t gpio_cc_dss;
39 };
40
41 struct gpio_cmsdk_ahb_dev_data {
42 /* gpio_driver_data needs to be first */
43 struct gpio_driver_data common;
44 /* list of callbacks */
45 sys_slist_t gpio_cb;
46 };
47
gpio_cmsdk_ahb_port_get_raw(const struct device * dev,uint32_t * value)48 static int gpio_cmsdk_ahb_port_get_raw(const struct device *dev,
49 uint32_t *value)
50 {
51 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
52
53 *value = cfg->port->data;
54
55 return 0;
56 }
57
gpio_cmsdk_ahb_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)58 static int gpio_cmsdk_ahb_port_set_masked_raw(const struct device *dev,
59 uint32_t mask,
60 uint32_t value)
61 {
62 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
63
64 cfg->port->dataout = (cfg->port->dataout & ~mask) | (mask & value);
65
66 return 0;
67 }
68
gpio_cmsdk_ahb_port_set_bits_raw(const struct device * dev,uint32_t mask)69 static int gpio_cmsdk_ahb_port_set_bits_raw(const struct device *dev,
70 uint32_t mask)
71 {
72 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
73
74 cfg->port->dataout |= mask;
75
76 return 0;
77 }
78
gpio_cmsdk_ahb_port_clear_bits_raw(const struct device * dev,uint32_t mask)79 static int gpio_cmsdk_ahb_port_clear_bits_raw(const struct device *dev,
80 uint32_t mask)
81 {
82 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
83
84 cfg->port->dataout &= ~mask;
85
86 return 0;
87 }
88
gpio_cmsdk_ahb_port_toggle_bits(const struct device * dev,uint32_t mask)89 static int gpio_cmsdk_ahb_port_toggle_bits(const struct device *dev,
90 uint32_t mask)
91 {
92 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
93
94 cfg->port->dataout ^= mask;
95
96 return 0;
97 }
98
cmsdk_ahb_gpio_config(const struct device * dev,uint32_t mask,gpio_flags_t flags)99 static int cmsdk_ahb_gpio_config(const struct device *dev, uint32_t mask,
100 gpio_flags_t flags)
101 {
102 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
103
104 if (((flags & GPIO_INPUT) == 0) && ((flags & GPIO_OUTPUT) == 0)) {
105 return -ENOTSUP;
106 }
107
108 if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) {
109 return -ENOTSUP;
110 }
111
112 if ((flags & GPIO_SINGLE_ENDED) != 0) {
113 return -ENOTSUP;
114 }
115
116 /*
117 * Setup the pin direction
118 * Output Enable:
119 * 0 - Input
120 * 1 - Output
121 */
122 if ((flags & GPIO_OUTPUT) != 0) {
123 if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
124 gpio_cmsdk_ahb_port_set_bits_raw(dev, mask);
125 } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
126 gpio_cmsdk_ahb_port_clear_bits_raw(dev, mask);
127 }
128 cfg->port->outenableset = mask;
129 } else {
130 cfg->port->outenableclr = mask;
131 }
132
133 cfg->port->altfuncclr = mask;
134
135 return 0;
136 }
137
138 /**
139 * @brief Configure pin or port
140 *
141 * @param dev Device struct
142 * @param pin The pin number
143 * @param flags Flags of pin or port
144 *
145 * @return 0 if successful, failed otherwise
146 */
gpio_cmsdk_ahb_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)147 static int gpio_cmsdk_ahb_config(const struct device *dev,
148 gpio_pin_t pin,
149 gpio_flags_t flags)
150 {
151 return cmsdk_ahb_gpio_config(dev, BIT(pin), flags);
152 }
153
gpio_cmsdk_ahb_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)154 static int gpio_cmsdk_ahb_pin_interrupt_configure(const struct device *dev,
155 gpio_pin_t pin,
156 enum gpio_int_mode mode,
157 enum gpio_int_trig trig)
158 {
159 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
160
161 if (trig == GPIO_INT_TRIG_BOTH) {
162 return -ENOTSUP;
163 }
164
165 /* For now treat level interrupts as not supported, as we seem to only
166 * get a single 'edge' still interrupt rather than continuous
167 * interrupts until the cause is cleared */
168 if (mode == GPIO_INT_MODE_LEVEL) {
169 return -ENOTSUP;
170 }
171
172 if (mode == GPIO_INT_MODE_DISABLED) {
173 cfg->port->intenclr = BIT(pin);
174 } else {
175 if (mode == GPIO_INT_MODE_EDGE) {
176 cfg->port->inttypeset = BIT(pin);
177 } else {
178 /* LEVEL */
179 cfg->port->inttypeclr = BIT(pin);
180 }
181
182 /* Level High or Edge Rising */
183 if (trig == GPIO_INT_TRIG_HIGH) {
184 cfg->port->intpolset = BIT(pin);
185 } else {
186 cfg->port->intpolclr = BIT(pin);
187 }
188 cfg->port->intclear = BIT(pin);
189 cfg->port->intenset = BIT(pin);
190 }
191
192 return 0;
193 }
194
gpio_cmsdk_ahb_isr(const struct device * dev)195 static void gpio_cmsdk_ahb_isr(const struct device *dev)
196 {
197 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
198 struct gpio_cmsdk_ahb_dev_data *data = dev->data;
199 uint32_t int_stat;
200
201 int_stat = cfg->port->intstatus;
202
203 /* clear the port interrupts */
204 cfg->port->intclear = int_stat;
205
206 gpio_fire_callbacks(&data->gpio_cb, dev, int_stat);
207
208 }
209
gpio_cmsdk_ahb_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)210 static int gpio_cmsdk_ahb_manage_callback(const struct device *dev,
211 struct gpio_callback *callback,
212 bool set)
213 {
214 struct gpio_cmsdk_ahb_dev_data *data = dev->data;
215
216 return gpio_manage_callback(&data->gpio_cb, callback, set);
217 }
218
219 static const struct gpio_driver_api gpio_cmsdk_ahb_drv_api_funcs = {
220 .pin_configure = gpio_cmsdk_ahb_config,
221 .port_get_raw = gpio_cmsdk_ahb_port_get_raw,
222 .port_set_masked_raw = gpio_cmsdk_ahb_port_set_masked_raw,
223 .port_set_bits_raw = gpio_cmsdk_ahb_port_set_bits_raw,
224 .port_clear_bits_raw = gpio_cmsdk_ahb_port_clear_bits_raw,
225 .port_toggle_bits = gpio_cmsdk_ahb_port_toggle_bits,
226 .pin_interrupt_configure = gpio_cmsdk_ahb_pin_interrupt_configure,
227 .manage_callback = gpio_cmsdk_ahb_manage_callback,
228 };
229
230 /**
231 * @brief Initialization function of GPIO
232 *
233 * @param dev Device struct
234 * @return 0 if successful, failed otherwise.
235 */
gpio_cmsdk_ahb_init(const struct device * dev)236 static int gpio_cmsdk_ahb_init(const struct device *dev)
237 {
238 const struct gpio_cmsdk_ahb_cfg * const cfg = dev->config;
239
240 #ifdef CONFIG_CLOCK_CONTROL
241 /* Enable clock for subsystem */
242 const struct device *const clk = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0));
243
244 if (!device_is_ready(clk)) {
245 return -ENODEV;
246 }
247
248 #ifdef CONFIG_SOC_SERIES_BEETLE
249 clock_control_on(clk, (clock_control_subsys_t) &cfg->gpio_cc_as);
250 clock_control_off(clk, (clock_control_subsys_t) &cfg->gpio_cc_ss);
251 clock_control_off(clk, (clock_control_subsys_t) &cfg->gpio_cc_dss);
252 #endif /* CONFIG_SOC_SERIES_BEETLE */
253 #endif /* CONFIG_CLOCK_CONTROL */
254
255 cfg->gpio_config_func(dev);
256
257 return 0;
258 }
259
260 #define CMSDK_AHB_GPIO_DEVICE(n) \
261 static void gpio_cmsdk_port_##n##_config_func(const struct device *dev); \
262 \
263 static const struct gpio_cmsdk_ahb_cfg gpio_cmsdk_port_##n##_config = { \
264 .common = { \
265 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
266 }, \
267 .port = ((volatile struct gpio_cmsdk_ahb *)DT_INST_REG_ADDR(n)),\
268 .gpio_config_func = gpio_cmsdk_port_##n##_config_func, \
269 .gpio_cc_as = {.bus = CMSDK_AHB, .state = SOC_ACTIVE, \
270 .device = DT_INST_REG_ADDR(n),}, \
271 .gpio_cc_ss = {.bus = CMSDK_AHB, .state = SOC_SLEEP, \
272 .device = DT_INST_REG_ADDR(n),}, \
273 .gpio_cc_dss = {.bus = CMSDK_AHB, .state = SOC_DEEPSLEEP, \
274 .device = DT_INST_REG_ADDR(n),}, \
275 }; \
276 \
277 static struct gpio_cmsdk_ahb_dev_data gpio_cmsdk_port_##n##_data; \
278 \
279 DEVICE_DT_INST_DEFINE(n, \
280 gpio_cmsdk_ahb_init, \
281 NULL, \
282 &gpio_cmsdk_port_##n##_data, \
283 &gpio_cmsdk_port_## n ##_config, \
284 PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
285 &gpio_cmsdk_ahb_drv_api_funcs); \
286 \
287 static void gpio_cmsdk_port_##n##_config_func(const struct device *dev) \
288 { \
289 IRQ_CONNECT(DT_INST_IRQN(n), \
290 DT_INST_IRQ(n, priority), \
291 gpio_cmsdk_ahb_isr, \
292 DEVICE_DT_INST_GET(n), 0); \
293 \
294 irq_enable(DT_INST_IRQN(n)); \
295 }
296
297 DT_INST_FOREACH_STATUS_OKAY(CMSDK_AHB_GPIO_DEVICE)
298