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