1 /*
2 * Copyright (C) 2017-2019 Alibaba Group Holding Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-08-20 zx.chen gpio driver
9 */
10
11 /******************************************************************************
12 * @file dw_gpio.c
13 * @brief CSI Source File for GPIO Driver
14 * @version V1.0
15 * @date 02. June 2017
16 ******************************************************************************/
17
18 #include <csi_config.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <drv_irq.h>
22 #include <drv_gpio.h>
23 #include <dw_gpio.h>
24 #include <csi_core.h>
25 #include <pin_name.h>
26
27 extern int32_t drv_pin_config_mode(port_name_e port, uint8_t offset, gpio_mode_e pin_mode);
28
29 #define ERR_GPIO(errno) (CSI_DRV_ERRNO_GPIO_BASE | errno)
30 #define GPIO_NULL_PARAM_CHK(para) HANDLE_PARAM_CHK(para, ERR_GPIO(DRV_ERROR_PARAMETER))
31
32 typedef void *gpio_port_handle_t;
33
34 typedef struct
35 {
36 #ifdef CONFIG_LPM
37 uint8_t gpio_power_status;
38 uint32_t gpio_regs_saved[7];
39 #endif
40 uint32_t base; ///< handle register base
41 uint32_t irq; ///< irq of this handle
42 uint32_t pin_num; ///< pin number of this handle
43 gpio_mode_e mode; ///< gpio mode
44 gpio_direction_e dir; ///< gpio direction
45 uint32_t mask; ///< gpio mask bit
46 uint32_t value; ///< gpio value
47 } dw_gpio_priv_t;
48
49 typedef struct
50 {
51 uint8_t portidx;
52 uint8_t idx;
53 uint8_t offset;
54 gpio_event_cb_t cb;
55 } dw_gpio_pin_priv_t;
56
57 extern int32_t target_gpio_port_init(port_name_e port, uint32_t *base, uint32_t *irq, void **handler, uint32_t *pin_num);
58 extern int32_t target_gpio_pin_init(int32_t gpio_pin, uint32_t *port_idx);
59
60 static dw_gpio_priv_t gpio_handle[CONFIG_GPIO_NUM];
61 static dw_gpio_pin_priv_t gpio_pin_handle[CONFIG_GPIO_PIN_NUM];
62
63 //
64 // Functions
65 //
gpio_set_direction(void * port,gpio_direction_e direction)66 static int32_t gpio_set_direction(
67 void *port,
68 gpio_direction_e direction
69 )
70 {
71 dw_gpio_priv_t *gpio_priv = port;
72 dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base);
73
74 if (direction == GPIO_DIRECTION_INPUT)
75 {
76 gpio_reg->SWPORT_DDR &= (~gpio_priv->mask);
77 } else if (direction == GPIO_DIRECTION_OUTPUT)
78 {
79 gpio_reg->SWPORT_DDR |= gpio_priv->mask;
80 } else
81 {
82 return ERR_GPIO(GPIO_ERROR_DIRECTION);
83 }
84
85 return 0;
86 }
87
88 /*
89 * Read the statu of the Port choosed.
90 * Parameters:
91 * port: use to choose a I/O port among Port A, B, or C.
92 * return: the value of the corresponding Port.
93 */
94
gpio_read(void * port,uint32_t * value)95 static int32_t gpio_read(void *port, uint32_t *value)
96 {
97 dw_gpio_priv_t *gpio_priv = port;
98 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_priv->base + 0x30);
99 *value = gpio_control_reg->EXT_PORTA;
100 return 0;
101 }
102
103
104 /*
105 * Write an output value to corresponding Port.
106 * Parameters:
107 * port: use to choose a I/O port among Port A, B, or C.
108 * output: value that will be written to the corresponding Port.
109 * return: SUCCESS
110 */
111
gpio_write(void * port,uint32_t mask)112 static int32_t gpio_write(void *port, uint32_t mask)
113 {
114 dw_gpio_priv_t *gpio_priv = port;
115 dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base);
116
117 uint32_t value = gpio_reg->SWPORT_DR;
118
119 value &= ~(mask);
120 value |= gpio_priv->value;
121 gpio_reg->SWPORT_DR = value;
122 return 0;
123 }
124
125 /**
126 * Configure a GPIO gpio_set_irq_mode.
127 * @param[in] pin the addr store the pin num.
128 * @param[in] _irqmode the irqmode of gpio
129 * @return zero on success. -1 on falure.
130 */
gpio_set_irq_mode(gpio_pin_handle_t pin,gpio_irq_mode_e irq_mode)131 static int32_t gpio_set_irq_mode(gpio_pin_handle_t pin, gpio_irq_mode_e irq_mode)
132 {
133 dw_gpio_pin_priv_t *gpio_pin_priv = pin;
134
135 /* convert portidx to port handle */
136 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
137
138 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
139 uint32_t offset = gpio_pin_priv->idx;
140 uint32_t mask = 1 << offset;
141
142 switch (irq_mode)
143 {
144 /* rising edge interrupt mode */
145 case GPIO_IRQ_MODE_RISING_EDGE:
146 gpio_control_reg->INTTYPE_LEVEL |= mask;
147 gpio_control_reg->INT_POLARITY |= mask;
148 break;
149
150 /* falling edge interrupt mode */
151 case GPIO_IRQ_MODE_FALLING_EDGE:
152 gpio_control_reg->INTTYPE_LEVEL |= mask;
153 gpio_control_reg->INT_POLARITY &= (~mask);
154 break;
155
156 /* low level interrupt mode */
157 case GPIO_IRQ_MODE_LOW_LEVEL:
158 gpio_control_reg->INTTYPE_LEVEL &= (~mask);
159 gpio_control_reg->INT_POLARITY &= (~mask);
160 break;
161
162 /* high level interrupt mode */
163 case GPIO_IRQ_MODE_HIGH_LEVEL:
164 gpio_control_reg->INTTYPE_LEVEL &= (~mask);
165 gpio_control_reg->INT_POLARITY |= mask;
166 break;
167
168 /* double edge interrupt mode */
169 case GPIO_IRQ_MODE_DOUBLE_EDGE:
170 return ERR_GPIO(DRV_ERROR_UNSUPPORTED);
171
172 default:
173 return ERR_GPIO(GPIO_ERROR_IRQ_MODE);
174 }
175
176 return 0;
177 }
178
179 /*
180 * Clear one or more interrupts of PortA.
181 * Parameters:
182 * pinno:
183 * return: SUCCESS.
184 */
185
gpio_irq_clear(gpio_pin_handle_t pin,uint32_t idx)186 static void gpio_irq_clear(gpio_pin_handle_t pin, uint32_t idx)
187 {
188 dw_gpio_pin_priv_t *gpio_pin_priv = pin;
189
190 /* convert portidx to port handle */
191 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
192
193 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
194
195 gpio_control_reg->PORTA_EOI = idx;
196 }
197
198
199 /*
200 * Enable one or more interrupts of PortA.
201 * Parameters:
202 * pinno:
203 * return: SUCCESS.
204 */
gpio_irq_enable(gpio_pin_handle_t pin)205 static void gpio_irq_enable(gpio_pin_handle_t pin)
206 {
207 dw_gpio_pin_priv_t *gpio_pin_priv = pin;
208
209 /* convert portidx to port handle */
210 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
211
212 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
213 uint32_t offset = gpio_pin_priv->idx;
214 uint32_t val = gpio_control_reg->INTEN;
215 val |= (1 << offset);
216 gpio_control_reg->INTEN = val;
217 }
218
219
220 /*
221 * Disable one or more interrupts of PortA.
222 * Parameters:
223 * pinno:
224 * return: SUCCESS.
225 */
226
gpio_irq_disable(gpio_pin_handle_t pin)227 static void gpio_irq_disable(gpio_pin_handle_t pin)
228 {
229 dw_gpio_pin_priv_t *gpio_pin_priv = pin;
230 uint32_t offset = gpio_pin_priv->idx;
231
232 /* convert portidx to port handle */
233 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
234
235 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
236 uint32_t val = gpio_control_reg->INTEN;
237 val &= ~(1 << offset);
238 gpio_control_reg->INTEN = val;
239 }
240
dw_gpio_irqhandler(int idx)241 void dw_gpio_irqhandler(int idx)
242 {
243 if (idx >= CONFIG_GPIO_NUM)
244 {
245 return;
246 }
247
248 dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_handle[idx].base + 0x30);
249
250 uint32_t value = gpio_control_reg->INTSTATUS;
251 uint8_t i;
252
253 /* find the interrput pin */
254 for (i = 0; i < 32; i++)
255 {
256 if (value & (1U << i))
257 {
258 uint32_t pin_idx = i;
259
260 #ifndef CONFIG_CHIP_DANICA
261 uint8_t j;
262
263 if (idx > 0)
264 {
265 for (j = 0; j < idx; j++)
266 {
267 pin_idx += gpio_handle[j].pin_num;
268 }
269 }
270
271 if (pin_idx >= CONFIG_GPIO_PIN_NUM)
272 {
273 return;
274 }
275
276 #endif
277 dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)&gpio_pin_handle[pin_idx];
278
279 gpio_irq_clear(gpio_pin_priv, (1 << i)); //clear the gpio interrupt
280
281 /* execute the callback function */
282 if ((gpio_event_cb_t)(gpio_pin_priv->cb))
283 {
284 ((gpio_event_cb_t)(gpio_pin_priv->cb))(gpio_pin_priv->offset);
285 }
286 }
287 }
288 }
289
290 /**
291 \brief Initialize GPIO module. 1. Initializes the resources needed for the GPIO handle 2.registers event callback function
292 3.get gpio_port_handle
293 \param[in] port port_name.
294 \return gpio_port_handle
295 */
csi_gpio_port_initialize(int32_t port)296 gpio_port_handle_t csi_gpio_port_initialize(int32_t port)
297 {
298 dw_gpio_priv_t *gpio_priv = NULL;
299
300 /* obtain the gpio port information */
301 uint32_t base = 0u;
302 uint32_t pin_num;
303 uint32_t irq;
304 void *handler;
305 int32_t idx = target_gpio_port_init(port, &base, &irq, &handler, &pin_num);
306
307 if (idx < 0 || idx >= CONFIG_GPIO_NUM)
308 {
309 return NULL;
310 }
311
312 gpio_priv = &gpio_handle[idx];
313
314 gpio_priv->base = base;
315 gpio_priv->irq = irq;
316 gpio_priv->pin_num = pin_num;
317
318 #ifdef CONFIG_LPM
319 csi_gpio_power_control(gpio_priv, DRV_POWER_FULL);
320 #endif
321
322 drv_irq_register(gpio_priv->irq, handler);
323 drv_irq_enable(gpio_priv->irq);
324
325 return (gpio_port_handle_t)gpio_priv;
326 }
327
328 /**
329 \brief De-initialize GPIO handle. stops operation and releases the software resources used by the handle
330 \param[in] handle gpio port handle to operate.
331 \return error code
332 */
csi_gpio_port_uninitialize(gpio_port_handle_t handle)333 int32_t csi_gpio_port_uninitialize(gpio_port_handle_t handle)
334 {
335 GPIO_NULL_PARAM_CHK(handle);
336
337 dw_gpio_priv_t *gpio_priv = handle;
338
339 drv_irq_disable(gpio_priv->irq);
340 drv_irq_unregister(gpio_priv->irq);
341
342 #ifdef CONFIG_LPM
343 csi_gpio_power_control(gpio_priv, DRV_POWER_OFF);
344 #endif
345
346 return 0;
347 }
348
349 #ifdef CONFIG_LPM
manage_clock(gpio_pin_handle_t handle,uint8_t enable)350 static void manage_clock(gpio_pin_handle_t handle, uint8_t enable)
351 {
352 dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
353 uint8_t device[] = {CLOCK_MANAGER_GPIO0, CLOCK_MANAGER_GPIO1};
354
355 drv_clock_manager_config(device[gpio_pin_priv->portidx], enable);
356 }
357
do_prepare_sleep_action(void * handle)358 static void do_prepare_sleep_action(void *handle)
359 {
360 dw_gpio_priv_t *gpio_handle = handle;
361 uint32_t *gbase = (uint32_t *)(gpio_handle->base);
362 uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30);
363 registers_save(gpio_handle->gpio_regs_saved, gbase, 3);
364 registers_save(&gpio_handle->gpio_regs_saved[3], control_base, 4);
365 }
366
do_wakeup_sleep_action(void * handle)367 static void do_wakeup_sleep_action(void *handle)
368 {
369 dw_gpio_priv_t *gpio_handle = handle;
370 uint32_t *gbase = (uint32_t *)(gpio_handle->base);
371 uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30);
372 registers_restore(gbase, gpio_handle->gpio_regs_saved, 3);
373 registers_restore(control_base, &gpio_handle->gpio_regs_saved[3], 4);
374 }
375 #endif
376
377 /**
378 \brief Initialize GPIO handle.
379 \param[in] gpio_pin Pointer to the int32_t.
380 \param[in] cb_event Pointer to \ref gpio_event_cb_t.
381 \param[in] arg Pointer to \ref arg used for the callback.
382 \return gpio_pin_handle
383 */
csi_gpio_pin_initialize(int32_t gpio_pin,gpio_event_cb_t cb_event)384 gpio_pin_handle_t csi_gpio_pin_initialize(int32_t gpio_pin, gpio_event_cb_t cb_event)
385 {
386
387 if (gpio_pin < 0 || gpio_pin >= CONFIG_GPIO_PIN_NUM)
388 {
389 return NULL;
390 }
391
392 uint32_t i;
393
394 for (i = 0; i < CONFIG_GPIO_NUM; i++)
395 {
396 csi_gpio_port_initialize(i);
397 }
398
399 /* obtain the gpio pin information */
400 uint32_t port_idx;
401 int32_t pin_idx = target_gpio_pin_init(gpio_pin, &port_idx);
402
403 if (pin_idx < 0)
404 {
405 return NULL;
406 }
407
408 int32_t idx = pin_idx;
409
410 for (i = 0; i < port_idx; i++)
411 {
412 idx += (gpio_handle[i].pin_num);
413 }
414
415 dw_gpio_pin_priv_t *gpio_pin_priv = &(gpio_pin_handle[idx]);
416 gpio_pin_priv->portidx = port_idx;
417
418
419 gpio_pin_priv->idx = pin_idx;
420 gpio_pin_priv->cb = cb_event;
421 gpio_pin_priv->offset = gpio_pin;
422
423 return (gpio_pin_handle_t)gpio_pin_priv;
424 }
425
426 /**
427 \brief De-initialize GPIO pin handle. stops operation and releases the software resources used by the handle
428 \param[in] handle gpio pin handle to operate.
429 \return error code
430 */
csi_gpio_pin_uninitialize(gpio_pin_handle_t handle)431 int32_t csi_gpio_pin_uninitialize(gpio_pin_handle_t handle)
432 {
433 if (handle == NULL)
434 {
435 return ERR_GPIO(DRV_ERROR_PARAMETER);
436 }
437
438 dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
439 gpio_pin_priv->cb = NULL;
440
441 gpio_irq_disable(handle);
442
443 return 0;
444 }
445
446 /**
447 \brief control gpio power.
448 \param[in] idx gpio index.
449 \param[in] state power state.\ref csi_power_stat_e.
450 \return error code
451 */
csi_gpio_power_control(gpio_pin_handle_t handle,csi_power_stat_e state)452 int32_t csi_gpio_power_control(gpio_pin_handle_t handle, csi_power_stat_e state)
453 {
454 GPIO_NULL_PARAM_CHK(handle);
455
456 #ifdef CONFIG_LPM
457 dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
458 power_cb_t callback =
459 {
460 .wakeup = do_wakeup_sleep_action,
461 .sleep = do_prepare_sleep_action,
462 .manage_clock = manage_clock
463 };
464 return drv_soc_power_control(&gpio_handle[gpio_pin_priv->portidx], state, &callback);
465 #else
466 return ERR_GPIO(DRV_ERROR_UNSUPPORTED);
467 #endif
468 }
469
470 /**
471 \brief config pin mode
472 \param[in] pin gpio pin handle to operate.
473 \param[in] mode \ref gpio_mode_e
474 \return error code
475 */
csi_gpio_pin_config_mode(gpio_pin_handle_t handle,gpio_mode_e mode)476 int32_t csi_gpio_pin_config_mode(gpio_pin_handle_t handle,
477 gpio_mode_e mode)
478 {
479 GPIO_NULL_PARAM_CHK(handle);
480
481 /* config the gpio pin mode direction mask bits */
482 dw_gpio_pin_priv_t *gpio_pin_priv = handle;
483
484 uint8_t offset = gpio_pin_priv->idx;
485
486 int32_t ret = drv_pin_config_mode(gpio_pin_priv->portidx, offset, mode);
487
488 return ret;
489 }
490 /**
491 \brief config pin direction
492 \param[in] pin gpio pin handle to operate.
493 \param[in] dir \ref gpio_direction_e
494 \return error code
495 */
csi_gpio_pin_config_direction(gpio_pin_handle_t handle,gpio_direction_e dir)496 int32_t csi_gpio_pin_config_direction(gpio_pin_handle_t handle,
497 gpio_direction_e dir)
498 {
499 GPIO_NULL_PARAM_CHK(handle);
500
501 /* config the gpio pin mode direction mask bits */
502 dw_gpio_pin_priv_t *gpio_pin_priv = handle;
503
504 /* convert portidx to port handle */
505 dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx];
506
507 gpio_priv->dir = dir;
508 gpio_priv->mask = 1 << gpio_pin_priv->idx;
509
510 uint32_t ret = gpio_set_direction(gpio_priv, dir);
511
512 if (ret)
513 {
514 return ret;
515 }
516
517 return 0;
518 }
519
520 /**
521 \brief config pin
522 \param[in] handle gpio pin handle to operate.
523 \param[in] mode \ref gpio_mode_e
524 \param[in] dir \ref gpio_direction_e
525 \return error code
526 */
csi_gpio_pin_config(gpio_pin_handle_t handle,gpio_mode_e mode,gpio_direction_e dir)527 int32_t csi_gpio_pin_config(gpio_pin_handle_t handle,
528 gpio_mode_e mode,
529 gpio_direction_e dir)
530 {
531 GPIO_NULL_PARAM_CHK(handle);
532
533 /* config the gpio pin mode direction mask bits */
534 dw_gpio_pin_priv_t *gpio_pin_priv = handle;
535
536 /* convert portidx to port handle */
537 dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx];
538
539 gpio_priv->mode = mode;
540 gpio_priv->dir = dir;
541 gpio_priv->mask = 1 << gpio_pin_priv->idx;
542
543 uint32_t ret = gpio_set_direction(gpio_priv, dir);
544
545 if (ret)
546 {
547 return ret;
548 }
549
550 return 0;
551
552 }
553
554 /**
555 \brief Set one or zero to the selected GPIO pin.
556 \param[in] handle gpio pin handle to operate.
557 \param[in] value the value to be set
558 \return error code
559 */
csi_gpio_pin_write(gpio_pin_handle_t handle,bool value)560 int32_t csi_gpio_pin_write(gpio_pin_handle_t handle, bool value)
561 {
562 GPIO_NULL_PARAM_CHK(handle);
563
564 dw_gpio_pin_priv_t *gpio_pin_priv = handle;
565
566 /* convert portidx to port handle */
567 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
568
569 uint8_t offset = gpio_pin_priv->idx;
570 uint32_t port_value = value << offset;
571
572 port_handle->value = port_value;
573 gpio_write(port_handle, (1 << offset));
574
575 return 0;
576
577 }
578
579 /**
580 \brief Get the value of selected GPIO pin.
581 \param[in] handle gpio pin handle to operate.
582 \param[out] value buf to store the pin value
583 \return error code
584 */
csi_gpio_pin_read(gpio_pin_handle_t handle,bool * value)585 int32_t csi_gpio_pin_read(gpio_pin_handle_t handle, bool *value)
586 {
587 GPIO_NULL_PARAM_CHK(handle);
588 GPIO_NULL_PARAM_CHK(value);
589
590 dw_gpio_pin_priv_t *gpio_pin_priv = handle;
591 uint32_t port_value;
592 uint8_t offset = gpio_pin_priv->idx;
593
594 /* convert portidx to port handle */
595 dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
596
597 gpio_read(port_handle, &port_value);
598 *value = (port_value & (1 << offset)) >> offset;
599
600 return 0;
601 }
602
603 /**
604 \brief set GPIO interrupt mode.
605 \param[in] handle gpio pin handle to operate.
606 \param[in] mode the irq mode to be set
607 \param[in] enable the enable flag
608 \return error code
609 */
csi_gpio_pin_set_irq(gpio_pin_handle_t handle,gpio_irq_mode_e mode,bool enable)610 int32_t csi_gpio_pin_set_irq(gpio_pin_handle_t handle, gpio_irq_mode_e mode, bool enable)
611 {
612 GPIO_NULL_PARAM_CHK(handle);
613
614 uint32_t ret = 0;
615
616 if (enable)
617 {
618 ret = gpio_set_irq_mode(handle, mode);
619
620 if (ret)
621 {
622 return ret;
623 }
624
625 gpio_irq_enable(handle);
626
627 } else
628 {
629 gpio_irq_disable(handle);
630
631 }
632
633 return ret;
634 }
635
636