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