1 /*
2 * Copyright (c) 2024 Microchip Technology Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_mec5_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/dt-bindings/gpio/gpio.h>
14 #include <zephyr/dt-bindings/pinctrl/mchp-xec-pinctrl.h>
15 #include <soc.h>
16 #include <zephyr/irq.h>
17 #include <mec_gpio_api.h>
18
19 #include <zephyr/drivers/gpio/gpio_utils.h>
20
21 /* 32 pins per bank. Each pin has a 4-byte control register */
22 #define MEC5_GPIO_PIN_CTRL_RSHFT 7
23 #define MEC5_GPIO_PIN_CTRL_ADDR_MSK 0xfu
24
25 struct gpio_mec5_data {
26 /* gpio_driver_data needs to be first */
27 struct gpio_driver_data common;
28 /* port ISR callback routine address */
29 sys_slist_t callbacks;
30 };
31
32 struct gpio_mec5_config {
33 /* gpio_driver_config needs to be first */
34 struct gpio_driver_config common;
35 uintptr_t pcr1_base;
36 uintptr_t parin_addr;
37 uintptr_t parout_addr;
38 uint32_t flags;
39 };
40
mec5_addr_to_port(uint32_t base_addr)41 static inline uint32_t mec5_addr_to_port(uint32_t base_addr)
42 {
43 return ((base_addr >> MEC5_GPIO_PIN_CTRL_RSHFT) & MEC5_GPIO_PIN_CTRL_ADDR_MSK);
44 }
45
46 /* NOTE: gpio_flags_t b[0:15] are defined in the dt-binding gpio header.
47 * b[31:16] are defined in the driver gpio header.
48 */
gpio_mec5_validate_flags(gpio_flags_t flags)49 static int gpio_mec5_validate_flags(gpio_flags_t flags)
50 {
51 if (flags & GPIO_LINE_OPEN_SOURCE) {
52 return -ENOTSUP;
53 }
54
55 if ((flags & GPIO_OUTPUT_INIT_LOW) && (flags & GPIO_OUTPUT_INIT_HIGH)) {
56 return -EINVAL;
57 }
58
59 return 0;
60 }
61
62 static const struct mec_gpio_props cfg_props_init[] = {
63 {MEC_GPIO_PWRGT_PROP_ID, MEC_GPIO_PROP_PWRGT_VTR},
64 {MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_CTRL},
65 {MEC_GPIO_INPAD_DIS_PROP_ID, MEC_GPIO_PROP_INPAD_EN},
66 };
67
68 /* Each GPIO pin has two 32-bit control registers. Control 1 configures pin
69 * features except for drive strength and slew rate in Control 2.
70 * A pin's input and output state can be read/written from either the Control 1
71 * register or from corresponding bits in the GPIO parallel input/output registers.
72 * The parallel input and output registers group 32 pins into each register.
73 * The GPIO hardware restricts the pin output state to Control 1 or the parallel bit.
74 * Both output bits reflect each other on read and writes but only one is writable
75 * selected by the output control select bit in Control 1. In the configuration API
76 * we use Control 1 to configure all pin features and output state. Before exiting,
77 * we set the output select for parallel mode enabling writes to the parallel output bit.
78 */
gpio_mec5_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)79 static int gpio_mec5_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
80 {
81 const struct gpio_mec5_config *config = dev->config;
82 uint32_t pin_num = 0u;
83 uint32_t port_num = 0u;
84 uint32_t temp = 0u;
85 int ret = 0;
86 size_t idx = 0;
87 struct mec_gpio_props props[8];
88
89 port_num = mec5_addr_to_port(config->pcr1_base);
90 ret = mec_hal_gpio_pin_num(port_num, pin, &pin_num);
91 if (ret != MEC_RET_OK) {
92 return -EINVAL;
93 }
94
95 ret = mec_hal_gpio_port_pin_valid(port_num, pin);
96 if (ret != MEC_RET_OK) {
97 return -EINVAL;
98 }
99
100 ret = gpio_mec5_validate_flags(flags);
101 if (ret) {
102 return ret;
103 }
104
105 if (flags == GPIO_DISCONNECTED) {
106 ret = mec_hal_gpio_set_property(pin_num, MEC_GPIO_PWRGT_PROP_ID,
107 MEC_GPIO_PROP_PWRGT_OFF);
108 if (ret != MEC_RET_OK) {
109 ret = -EIO;
110 }
111 return ret;
112 }
113
114 ret = mec_hal_gpio_set_props(pin_num, cfg_props_init, ARRAY_SIZE(cfg_props_init));
115 if (ret != MEC_RET_OK) {
116 return -EIO;
117 }
118
119 if (flags & GPIO_OUTPUT) {
120 props[idx].prop = MEC_GPIO_DIR_PROP_ID;
121 props[idx].val = MEC_GPIO_PROP_DIR_OUT;
122 idx++;
123
124 props[idx].prop = MEC_GPIO_OBUFT_PROP_ID;
125 props[idx].val = MEC_GPIO_PROP_PUSH_PULL;
126 if (flags & GPIO_LINE_OPEN_DRAIN) {
127 props[idx].val = MEC_GPIO_PROP_OPEN_DRAIN;
128 }
129 idx++;
130
131 props[idx].prop = MEC_GPIO_CTRL_OUT_VAL_ID;
132 props[idx].val = 0u;
133 if (flags & GPIO_OUTPUT_INIT_HIGH) {
134 props[idx].val = 1u;
135 } else if (flags & GPIO_OUTPUT_INIT_LOW) {
136 props[idx].val = 0u;
137 } else {
138 mec_hal_gpio_pad_in(pin_num, &props[idx].val);
139 }
140 idx++;
141 }
142
143 if (flags & GPIO_INPUT) {
144 props[idx].prop = MEC_GPIO_DIR_PROP_ID;
145 props[idx].val = MEC_GPIO_PROP_DIR_IN;
146 idx++;
147 }
148
149 temp = flags & (GPIO_PULL_UP | GPIO_PULL_DOWN);
150 if (temp) {
151 props[idx].prop = MEC_GPIO_PUD_PROP_ID;
152 if (temp == (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
153 props[idx].val = MEC_GPIO_PROP_REPEATER;
154 } else if (temp & GPIO_PULL_UP) {
155 props[idx].val = MEC_GPIO_PROP_PULL_UP;
156 } else {
157 props[idx].val = MEC_GPIO_PROP_PULL_DN;
158 }
159 idx++;
160 }
161
162 ret = mec_hal_gpio_set_props(pin_num, props, idx);
163 if (ret != MEC_RET_OK) {
164 return -EIO;
165 }
166
167 /* make output state in control read-only in control and read-write in parallel reg */
168 ret = mec_hal_gpio_set_property(pin_num, MEC_GPIO_OSEL_PROP_ID, MEC_GPIO_PROP_OSEL_PAROUT);
169 if (ret != MEC_RET_OK) {
170 return -EIO;
171 }
172
173 return 0;
174 }
175
gen_gpio_ctrl_icfg(enum gpio_int_mode mode,enum gpio_int_trig trig)176 static uint8_t gen_gpio_ctrl_icfg(enum gpio_int_mode mode, enum gpio_int_trig trig)
177 {
178 uint8_t idet;
179
180 if (mode == GPIO_INT_MODE_DISABLED) {
181 idet = MEC_GPIO_PROP_IDET_DIS;
182 } else {
183 if (mode == GPIO_INT_MODE_LEVEL) {
184 if (trig == GPIO_INT_TRIG_HIGH) {
185 idet = MEC_GPIO_PROP_IDET_HI_LVL;
186 } else {
187 idet = MEC_GPIO_PROP_IDET_LO_LVL;
188 }
189 } else {
190 switch (trig) {
191 case GPIO_INT_TRIG_LOW:
192 idet = MEC_GPIO_PROP_IDET_EDGE_DN;
193 break;
194 case GPIO_INT_TRIG_HIGH:
195 idet = MEC_GPIO_PROP_IDET_EDGE_UP;
196 break;
197 case GPIO_INT_TRIG_BOTH:
198 idet = MEC_GPIO_PROP_IDET_EDGE_BOTH;
199 break;
200 default:
201 idet = MEC_GPIO_PROP_IDET_DIS;
202 break;
203 }
204 }
205 }
206
207 return idet;
208 }
209
210 /* Enable interrupt to propagate via its GIRQ to the NVIC */
gpio_mec5_intr_en(uint8_t port,gpio_pin_t pin,enum gpio_int_mode mode)211 static void gpio_mec5_intr_en(uint8_t port, gpio_pin_t pin, enum gpio_int_mode mode)
212 {
213 uint8_t en = 0u;
214
215 if (mode != GPIO_INT_MODE_DISABLED) {
216 en = 1u;
217 }
218
219 mec_hal_gpio_port_pin_ia_enable(port, pin, en);
220 }
221
222 static const struct mec_gpio_props icfg_props_init[] = {
223 {MEC_GPIO_PWRGT_PROP_ID, MEC_GPIO_PROP_PWRGT_VTR},
224 {MEC_GPIO_INPAD_DIS_PROP_ID, MEC_GPIO_PROP_INPAD_EN},
225 };
226
gpio_mec5_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)227 static int gpio_mec5_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
228 enum gpio_int_mode mode, enum gpio_int_trig trig)
229 {
230 const struct gpio_mec5_config *config = dev->config;
231 uint32_t pin_num = (uint32_t)MEC_PIN_MAX;
232 uint32_t port_num = 0;
233 int ret = 0;
234 uint8_t idet = 0, idet_curr = 0;
235
236 /* Validate pin number range in terms of current port */
237 port_num = mec5_addr_to_port(config->pcr1_base);
238 ret = mec_hal_gpio_port_pin_valid(port_num, pin);
239 if (ret != MEC_RET_OK) {
240 return -EINVAL;
241 }
242
243 /* Check if GPIO port supports interrupts */
244 if ((mode != GPIO_INT_MODE_DISABLED) && !(config->flags & GPIO_INT_ENABLE)) {
245 return -ENOTSUP;
246 }
247
248 /* Disable interrupt in the EC aggregator */
249 gpio_mec5_intr_en(port_num, pin, 0);
250
251 mec_hal_gpio_pin_num(port_num, pin, &pin_num);
252 ret = mec_hal_gpio_set_props(pin_num, icfg_props_init, ARRAY_SIZE(icfg_props_init));
253 if (ret) {
254 return -EIO;
255 }
256
257 idet_curr = MEC_GPIO_PROP_IDET_DIS;
258 ret = mec_hal_gpio_get_property(pin_num, MEC_GPIO_IDET_PROP_ID, &idet_curr);
259 if (ret != MEC_RET_OK) {
260 return -EIO;
261 }
262
263 idet = gen_gpio_ctrl_icfg(mode, trig);
264 if (idet_curr == idet) {
265 gpio_mec5_intr_en(port_num, pin, mode);
266 return 0;
267 }
268
269 ret = mec_hal_gpio_set_property(pin_num, MEC_GPIO_IDET_PROP_ID, idet);
270 if (ret != MEC_RET_OK) {
271 return -EIO;
272 }
273
274 ret = mec_hal_gpio_pin_ia_status_clr(pin_num);
275 if (ret != MEC_RET_OK) {
276 return -EIO;
277 }
278
279 gpio_mec5_intr_en(port_num, pin, mode);
280
281 return 0;
282 }
283
gpio_mec5_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)284 static int gpio_mec5_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value)
285 {
286 const struct gpio_mec5_config *config = dev->config;
287 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
288 int ret = mec_hal_gpio_parout_port_mask(port_num, value, (const uint32_t)mask);
289
290 if (ret != MEC_RET_OK) {
291 return -EIO;
292 }
293
294 return 0;
295 }
296
gpio_mec5_port_set_bits_raw(const struct device * dev,uint32_t mask)297 static int gpio_mec5_port_set_bits_raw(const struct device *dev, uint32_t mask)
298 {
299 const struct gpio_mec5_config *config = dev->config;
300 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
301 int ret = mec_hal_gpio_parout_port_set_bits(port_num, (const uint32_t)mask);
302
303 if (ret != MEC_RET_OK) {
304 return -EIO;
305 }
306
307 return 0;
308 }
309
gpio_mec5_port_clear_bits_raw(const struct device * dev,uint32_t mask)310 static int gpio_mec5_port_clear_bits_raw(const struct device *dev, uint32_t mask)
311 {
312 const struct gpio_mec5_config *config = dev->config;
313 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
314 int ret = mec_hal_gpio_parout_port_mask(port_num, 0u, (const uint32_t)mask);
315
316 if (ret != MEC_RET_OK) {
317 return -EIO;
318 }
319
320 return 0;
321 }
322
gpio_mec5_port_toggle_bits(const struct device * dev,uint32_t mask)323 static int gpio_mec5_port_toggle_bits(const struct device *dev, uint32_t mask)
324 {
325 const struct gpio_mec5_config *config = dev->config;
326 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
327
328 if (mec_hal_gpio_parout_port_xor(port_num, mask) != MEC_RET_OK) {
329 return -EIO;
330 }
331
332 return 0;
333 }
334
gpio_mec5_port_get_raw(const struct device * dev,uint32_t * value)335 static int gpio_mec5_port_get_raw(const struct device *dev, uint32_t *value)
336 {
337 const struct gpio_mec5_config *config = dev->config;
338 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
339
340 if (mec_hal_gpio_parin_port(port_num, value) != MEC_RET_OK) {
341 return -EIO;
342 }
343
344 return 0;
345 }
346
gpio_mec5_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)347 static int gpio_mec5_manage_callback(const struct device *dev, struct gpio_callback *callback,
348 bool set)
349 {
350 struct gpio_mec5_data *data = dev->data;
351
352 gpio_manage_callback(&data->callbacks, callback, set);
353
354 return 0;
355 }
356
357 #ifdef CONFIG_GPIO_GET_DIRECTION
gpio_mec5_get_direction(const struct device * port,gpio_port_pins_t map,gpio_port_pins_t * inputs,gpio_port_pins_t * outputs)358 static int gpio_mec5_get_direction(const struct device *port, gpio_port_pins_t map,
359 gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
360 {
361 if (!port) {
362 return -EINVAL;
363 }
364
365 const struct gpio_mec5_config *config = port->config;
366 uint32_t valid_msk = 0u;
367 uint32_t pin_num = 0u, port_num = 0u;
368 int ret = 0;
369 uint8_t pwr_gate = 0u, dir = 0u, in_pad_dis = 0u;
370
371 port_num = mec5_addr_to_port(config->pcr1_base);
372 ret = mec_hal_gpio_port_valid_mask(port_num, &valid_msk);
373 if (ret != MEC_RET_OK) {
374 return -EIO;
375 }
376
377 *inputs = 0u;
378 *outputs = 0u;
379 for (uint8_t pin_pos = 0; pin_pos < 32; pin++) {
380 if (!map) {
381 break;
382 }
383 if ((map & BIT(pin_pos)) && (valid_msk & BIT(pin_pos))) {
384 mec_hal_gpio_pin_num(port_num, pin, &pin_num);
385 mec_hal_gpio_get_property(pin_num, MEC_GPIO_PWRGT_PROP_ID, &pwr_gate);
386 mec_hal_gpio_get_property(pin_num, MEC_GPIO_DIR_PROP_ID, &dir);
387 mec_hal_gpio_get_property(pin_num, MEC_GPIO_INPAD_DIS_PROP_ID, &in_pad_dis);
388
389 if (pwr_gate != MEC_GPIO_PROP_PWRGT_OFF) {
390 if (outputs && (dir == MEC_GPIO_PROP_DIR_OUT)) {
391 *outputs |= BIT(pin_pos);
392 } else if (inputs && (in_pad_dis == MEC_GPIO_PROP_INPAD_EN)) {
393 *inputs |= BIT(pin_pos);
394 }
395 }
396
397 map &= ~BIT(pin_pos);
398 }
399 }
400
401 return 0;
402 }
403 #endif
404
405 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_mec5_get_config(const struct device * port,gpio_pin_t pin,gpio_flags_t * flags)406 int gpio_mec5_get_config(const struct device *port, gpio_pin_t pin, gpio_flags_t *flags)
407 {
408 if (!port || !flags) {
409 return -EINVAL;
410 }
411
412 const struct gpio_mec5_config *config = port->config;
413 uint32_t port_num = mec5_addr_to_port(config->pcr1_base);
414 int ret = mec_hal_gpio_port_pin_valid(port_num, pin);
415
416 if (ret != MEC_RET_OK) {
417 return -EINVAL;
418 }
419
420 uint32_t pin_ctrl = mec_hal_gpio_port_get_ctrl_nc(port_num, pin);
421 uint32_t pin_flags = 0u;
422 uint8_t prop = MEC_GPIO_PROP_DIR_IN;
423
424 mec_hal_gpio_get_ctrl_property(pin_ctrl, MEC_GPIO_DIR_PROP_ID, &prop);
425
426 if (prop == MEC_GPIO_PROP_DIR_OUT) {
427 pin_flags |= GPIO_OUTPUT;
428 mec_hal_gpio_get_ctrl_property(pin_ctrl, MEC_GPIO_CTRL_OUT_VAL_ID, &prop);
429 if (prop != 0) {
430 pin_flags |= GPIO_OUTPUT_INIT_HIGH;
431 } else {
432 pin_flags |= GPIO_OUTPUT_INIT_LOW;
433 }
434
435 prop = MEC_GPIO_PROP_PUSH_PULL;
436 mec_hal_gpio_get_ctrl_property(pin_ctrl, MEC_GPIO_OBUFT_PROP_ID, &prop);
437 if (prop == MEC_GPIO_PROP_OPEN_DRAIN) {
438 pin_flags |= GPIO_OPEN_DRAIN;
439 }
440 } else {
441 prop = MEC_GPIO_PROP_INPAD_DIS;
442 mec_hal_gpio_get_ctrl_property(pin_ctrl, MEC_GPIO_INPAD_DIS_PROP_ID, &prop);
443 if (prop == MEC_GPIO_PROP_INPAD_EN) {
444 pin_flags |= GPIO_INPUT;
445 }
446 }
447
448 if (pin_flags) {
449 *flags = pin_flags;
450 } else {
451 *flags = GPIO_DISCONNECTED;
452 }
453
454 return 0;
455 }
456 #endif
457
gpio_mec5_port_isr(const struct device * dev)458 static void gpio_mec5_port_isr(const struct device *dev)
459 {
460 const struct gpio_mec5_config *config = dev->config;
461 struct gpio_mec5_data *data = dev->data;
462 uint32_t girq_result = 0xffffffffu;
463 uint32_t port_num = 0u;
464
465 /* Figure out which interrupts have been triggered from the EC
466 * aggregator result register
467 */
468 port_num = mec5_addr_to_port(config->pcr1_base);
469 mec_hal_gpio_port_ia_result(port_num, &girq_result);
470
471 /* Clear source register in aggregator before firing callbacks */
472 mec_hal_gpio_port_ia_status_clr_mask(port_num, girq_result);
473
474 gpio_fire_callbacks(&data->callbacks, dev, girq_result);
475 }
476
477 /* GPIO driver official API table */
478 static const struct gpio_driver_api gpio_mec5_driver_api = {
479 .pin_configure = gpio_mec5_configure,
480 .port_get_raw = gpio_mec5_port_get_raw,
481 .port_set_masked_raw = gpio_mec5_port_set_masked_raw,
482 .port_set_bits_raw = gpio_mec5_port_set_bits_raw,
483 .port_clear_bits_raw = gpio_mec5_port_clear_bits_raw,
484 .port_toggle_bits = gpio_mec5_port_toggle_bits,
485 .pin_interrupt_configure = gpio_mec5_pin_interrupt_configure,
486 .manage_callback = gpio_mec5_manage_callback,
487 #ifdef CONFIG_GPIO_GET_DIRECTION
488 .port_get_direction = gpio_mec5_get_direction,
489 #endif
490 #ifdef CONFIG_GPIO_GET_CONFIG
491 .pin_get_config = gpio_mec5_get_config,
492 #endif
493 };
494
495 #define MEC5_GPIO_PORT_FLAGS(n) ((DT_INST_IRQ_HAS_CELL(n, irq)) ? GPIO_INT_ENABLE : 0)
496
497 /* TODO remove port_num. Derive it from pin & pin_num? */
498 #define MEC5_GPIO_PORT(n) \
499 static int gpio_mec5_port_init_##n(const struct device *dev) \
500 { \
501 if (!(DT_INST_IRQ_HAS_CELL(n, irq))) { \
502 return 0; \
503 } \
504 \
505 const struct gpio_mec5_config *config = dev->config; \
506 uint32_t port_num = mec5_addr_to_port(config->pcr1_base); \
507 \
508 mec_hal_gpio_port_ia_ctrl(port_num, 1u); \
509 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), gpio_mec5_port_isr, \
510 DEVICE_DT_INST_GET(n), 0u); \
511 irq_enable(DT_INST_IRQN(n)); \
512 return 0; \
513 } \
514 static struct gpio_mec5_data gpio_mec5_port_data_##n; \
515 static const struct gpio_mec5_config gpio_mec5_config_##n = { \
516 .common = \
517 { \
518 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
519 }, \
520 .pcr1_base = (uintptr_t)DT_INST_REG_ADDR_BY_IDX(n, 0), \
521 .parin_addr = (uintptr_t)DT_INST_REG_ADDR_BY_IDX(n, 1), \
522 .parout_addr = (uintptr_t)DT_INST_REG_ADDR_BY_IDX(n, 2), \
523 .flags = MEC5_GPIO_PORT_FLAGS(n), \
524 }; \
525 DEVICE_DT_INST_DEFINE(n, gpio_mec5_port_init_##n, NULL, &gpio_mec5_port_data_##n, \
526 &gpio_mec5_config_##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \
527 &gpio_mec5_driver_api);
528
529 DT_INST_FOREACH_STATUS_OKAY(MEC5_GPIO_PORT)
530