diff options
Diffstat (limited to 'bsp/thead-smart/drivers/dw_gpio.c')
| -rw-r--r-- | bsp/thead-smart/drivers/dw_gpio.c | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/bsp/thead-smart/drivers/dw_gpio.c b/bsp/thead-smart/drivers/dw_gpio.c new file mode 100644 index 0000000000..9ee3a3e429 --- /dev/null +++ b/bsp/thead-smart/drivers/dw_gpio.c @@ -0,0 +1,635 @@ +/* + * Copyright (C) 2017-2019 Alibaba Group Holding Limited + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-08-20 zx.chen gpio driver + */ + +/****************************************************************************** + * @file dw_gpio.c + * @brief CSI Source File for GPIO Driver + * @version V1.0 + * @date 02. June 2017 + ******************************************************************************/ + +#include <csi_config.h> +#include <stdbool.h> +#include <stdio.h> +#include <drv_irq.h> +#include <drv_gpio.h> +#include <dw_gpio.h> +#include <csi_core.h> +#include <pin_name.h> + +extern int32_t drv_pin_config_mode(port_name_e port, uint8_t offset, gpio_mode_e pin_mode); + +#define ERR_GPIO(errno) (CSI_DRV_ERRNO_GPIO_BASE | errno) +#define GPIO_NULL_PARAM_CHK(para) HANDLE_PARAM_CHK(para, ERR_GPIO(DRV_ERROR_PARAMETER)) + +typedef void *gpio_port_handle_t; + +typedef struct +{ +#ifdef CONFIG_LPM + uint8_t gpio_power_status; + uint32_t gpio_regs_saved[7]; +#endif + uint32_t base; ///< handle register base + uint32_t irq; ///< irq of this handle + uint32_t pin_num; ///< pin number of this handle + gpio_mode_e mode; ///< gpio mode + gpio_direction_e dir; ///< gpio direction + uint32_t mask; ///< gpio mask bit + uint32_t value; ///< gpio value +} dw_gpio_priv_t; + +typedef struct +{ + uint8_t portidx; + uint8_t idx; + uint8_t offset; + gpio_event_cb_t cb; +} dw_gpio_pin_priv_t; + +extern int32_t target_gpio_port_init(port_name_e port, uint32_t *base, uint32_t *irq, void **handler, uint32_t *pin_num); +extern int32_t target_gpio_pin_init(int32_t gpio_pin, uint32_t *port_idx); + +static dw_gpio_priv_t gpio_handle[CONFIG_GPIO_NUM]; +static dw_gpio_pin_priv_t gpio_pin_handle[CONFIG_GPIO_PIN_NUM]; + +// +// Functions +// +static int32_t gpio_set_direction( + void *port, + gpio_direction_e direction +) +{ + dw_gpio_priv_t *gpio_priv = port; + dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base); + + if (direction == GPIO_DIRECTION_INPUT) + { + gpio_reg->SWPORT_DDR &= (~gpio_priv->mask); + } else if (direction == GPIO_DIRECTION_OUTPUT) + { + gpio_reg->SWPORT_DDR |= gpio_priv->mask; + } else + { + return ERR_GPIO(GPIO_ERROR_DIRECTION); + } + + return 0; +} + +/* + * Read the statu of the Port choosed. + * Parameters: + * port: use to choose a I/O port among Port A, B, or C. + * return: the value of the corresponding Port. + */ + +static int32_t gpio_read(void *port, uint32_t *value) +{ + dw_gpio_priv_t *gpio_priv = port; + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_priv->base + 0x30); + *value = gpio_control_reg->EXT_PORTA; + return 0; +} + + +/* + * Write an output value to corresponding Port. + * Parameters: + * port: use to choose a I/O port among Port A, B, or C. + * output: value that will be written to the corresponding Port. + * return: SUCCESS + */ + +static int32_t gpio_write(void *port, uint32_t mask) +{ + dw_gpio_priv_t *gpio_priv = port; + dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base); + + uint32_t value = gpio_reg->SWPORT_DR; + + value &= ~(mask); + value |= gpio_priv->value; + gpio_reg->SWPORT_DR = value; + return 0; +} + +/** + * Configure a GPIO gpio_set_irq_mode. + * @param[in] pin the addr store the pin num. + * @param[in] _irqmode the irqmode of gpio + * @return zero on success. -1 on falure. + */ +static int32_t gpio_set_irq_mode(gpio_pin_handle_t pin, gpio_irq_mode_e irq_mode) +{ + dw_gpio_pin_priv_t *gpio_pin_priv = pin; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30); + uint32_t offset = gpio_pin_priv->idx; + uint32_t mask = 1 << offset; + + switch (irq_mode) + { + /* rising edge interrupt mode */ + case GPIO_IRQ_MODE_RISING_EDGE: + gpio_control_reg->INTTYPE_LEVEL |= mask; + gpio_control_reg->INT_POLARITY |= mask; + break; + + /* falling edge interrupt mode */ + case GPIO_IRQ_MODE_FALLING_EDGE: + gpio_control_reg->INTTYPE_LEVEL |= mask; + gpio_control_reg->INT_POLARITY &= (~mask); + break; + + /* low level interrupt mode */ + case GPIO_IRQ_MODE_LOW_LEVEL: + gpio_control_reg->INTTYPE_LEVEL &= (~mask); + gpio_control_reg->INT_POLARITY &= (~mask); + break; + + /* high level interrupt mode */ + case GPIO_IRQ_MODE_HIGH_LEVEL: + gpio_control_reg->INTTYPE_LEVEL &= (~mask); + gpio_control_reg->INT_POLARITY |= mask; + break; + + /* double edge interrupt mode */ + case GPIO_IRQ_MODE_DOUBLE_EDGE: + return ERR_GPIO(DRV_ERROR_UNSUPPORTED); + + default: + return ERR_GPIO(GPIO_ERROR_IRQ_MODE); + } + + return 0; +} + +/* + * Clear one or more interrupts of PortA. + * Parameters: + * pinno: + * return: SUCCESS. + */ + +static void gpio_irq_clear(gpio_pin_handle_t pin, uint32_t idx) +{ + dw_gpio_pin_priv_t *gpio_pin_priv = pin; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30); + + gpio_control_reg->PORTA_EOI = idx; +} + + +/* + * Enable one or more interrupts of PortA. + * Parameters: + * pinno: + * return: SUCCESS. + */ +static void gpio_irq_enable(gpio_pin_handle_t pin) +{ + dw_gpio_pin_priv_t *gpio_pin_priv = pin; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30); + uint32_t offset = gpio_pin_priv->idx; + uint32_t val = gpio_control_reg->INTEN; + val |= (1 << offset); + gpio_control_reg->INTEN = val; +} + + +/* + * Disable one or more interrupts of PortA. + * Parameters: + * pinno: + * return: SUCCESS. + */ + +static void gpio_irq_disable(gpio_pin_handle_t pin) +{ + dw_gpio_pin_priv_t *gpio_pin_priv = pin; + uint32_t offset = gpio_pin_priv->idx; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30); + uint32_t val = gpio_control_reg->INTEN; + val &= ~(1 << offset); + gpio_control_reg->INTEN = val; +} + +void dw_gpio_irqhandler(int idx) +{ + if (idx >= CONFIG_GPIO_NUM) + { + return; + } + + dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_handle[idx].base + 0x30); + + uint32_t value = gpio_control_reg->INTSTATUS; + uint8_t i; + + /* find the interrput pin */ + for (i = 0; i < 32; i++) + { + if (value & (1U << i)) + { + uint32_t pin_idx = i; + +#ifndef CONFIG_CHIP_DANICA + uint8_t j; + + if (idx > 0) + { + for (j = 0; j < idx; j++) + { + pin_idx += gpio_handle[j].pin_num; + } + } + + if (pin_idx >= CONFIG_GPIO_PIN_NUM) + { + return; + } + +#endif + dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)&gpio_pin_handle[pin_idx]; + + gpio_irq_clear(gpio_pin_priv, (1 << i)); //clear the gpio interrupt + + /* execute the callback function */ + if ((gpio_event_cb_t)(gpio_pin_priv->cb)) + { + ((gpio_event_cb_t)(gpio_pin_priv->cb))(gpio_pin_priv->offset); + } + } + } +} + +/** + \brief Initialize GPIO module. 1. Initializes the resources needed for the GPIO handle 2.registers event callback function + 3.get gpio_port_handle + \param[in] port port_name. + \return gpio_port_handle +*/ +gpio_port_handle_t csi_gpio_port_initialize(int32_t port) +{ + dw_gpio_priv_t *gpio_priv = NULL; + + /* obtain the gpio port information */ + uint32_t base = 0u; + uint32_t pin_num; + uint32_t irq; + void *handler; + int32_t idx = target_gpio_port_init(port, &base, &irq, &handler, &pin_num); + + if (idx < 0 || idx >= CONFIG_GPIO_NUM) + { + return NULL; + } + + gpio_priv = &gpio_handle[idx]; + + gpio_priv->base = base; + gpio_priv->irq = irq; + gpio_priv->pin_num = pin_num; + +#ifdef CONFIG_LPM + csi_gpio_power_control(gpio_priv, DRV_POWER_FULL); +#endif + + drv_irq_register(gpio_priv->irq, handler); + drv_irq_enable(gpio_priv->irq); + + return (gpio_port_handle_t)gpio_priv; +} + +/** + \brief De-initialize GPIO handle. stops operation and releases the software resources used by the handle + \param[in] handle gpio port handle to operate. + \return error code +*/ +int32_t csi_gpio_port_uninitialize(gpio_port_handle_t handle) +{ + GPIO_NULL_PARAM_CHK(handle); + + dw_gpio_priv_t *gpio_priv = handle; + + drv_irq_disable(gpio_priv->irq); + drv_irq_unregister(gpio_priv->irq); + +#ifdef CONFIG_LPM + csi_gpio_power_control(gpio_priv, DRV_POWER_OFF); +#endif + + return 0; +} + +#ifdef CONFIG_LPM +static void manage_clock(gpio_pin_handle_t handle, uint8_t enable) +{ + dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle; + uint8_t device[] = {CLOCK_MANAGER_GPIO0, CLOCK_MANAGER_GPIO1}; + + drv_clock_manager_config(device[gpio_pin_priv->portidx], enable); +} + +static void do_prepare_sleep_action(void *handle) +{ + dw_gpio_priv_t *gpio_handle = handle; + uint32_t *gbase = (uint32_t *)(gpio_handle->base); + uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30); + registers_save(gpio_handle->gpio_regs_saved, gbase, 3); + registers_save(&gpio_handle->gpio_regs_saved[3], control_base, 4); +} + +static void do_wakeup_sleep_action(void *handle) +{ + dw_gpio_priv_t *gpio_handle = handle; + uint32_t *gbase = (uint32_t *)(gpio_handle->base); + uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30); + registers_restore(gbase, gpio_handle->gpio_regs_saved, 3); + registers_restore(control_base, &gpio_handle->gpio_regs_saved[3], 4); +} +#endif + +/** + \brief Initialize GPIO handle. + \param[in] gpio_pin Pointer to the int32_t. + \param[in] cb_event Pointer to \ref gpio_event_cb_t. + \param[in] arg Pointer to \ref arg used for the callback. + \return gpio_pin_handle +*/ +gpio_pin_handle_t csi_gpio_pin_initialize(int32_t gpio_pin, gpio_event_cb_t cb_event) +{ + + if (gpio_pin < 0 || gpio_pin >= CONFIG_GPIO_PIN_NUM) + { + return NULL; + } + + uint32_t i; + + for (i = 0; i < CONFIG_GPIO_NUM; i++) + { + csi_gpio_port_initialize(i); + } + + /* obtain the gpio pin information */ + uint32_t port_idx; + int32_t pin_idx = target_gpio_pin_init(gpio_pin, &port_idx); + + if (pin_idx < 0) + { + return NULL; + } + + int32_t idx = pin_idx; + + for (i = 0; i < port_idx; i++) + { + idx += (gpio_handle[i].pin_num); + } + + dw_gpio_pin_priv_t *gpio_pin_priv = &(gpio_pin_handle[idx]); + gpio_pin_priv->portidx = port_idx; + + + gpio_pin_priv->idx = pin_idx; + gpio_pin_priv->cb = cb_event; + gpio_pin_priv->offset = gpio_pin; + + return (gpio_pin_handle_t)gpio_pin_priv; +} + +/** + \brief De-initialize GPIO pin handle. stops operation and releases the software resources used by the handle + \param[in] handle gpio pin handle to operate. + \return error code +*/ +int32_t csi_gpio_pin_uninitialize(gpio_pin_handle_t handle) +{ + if (handle == NULL) + { + return ERR_GPIO(DRV_ERROR_PARAMETER); + } + + dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle; + gpio_pin_priv->cb = NULL; + + gpio_irq_disable(handle); + + return 0; +} + +/** + \brief control gpio power. + \param[in] idx gpio index. + \param[in] state power state.\ref csi_power_stat_e. + \return error code +*/ +int32_t csi_gpio_power_control(gpio_pin_handle_t handle, csi_power_stat_e state) +{ + GPIO_NULL_PARAM_CHK(handle); + +#ifdef CONFIG_LPM + dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle; + power_cb_t callback = + { + .wakeup = do_wakeup_sleep_action, + .sleep = do_prepare_sleep_action, + .manage_clock = manage_clock + }; + return drv_soc_power_control(&gpio_handle[gpio_pin_priv->portidx], state, &callback); +#else + return ERR_GPIO(DRV_ERROR_UNSUPPORTED); +#endif +} + +/** + \brief config pin mode + \param[in] pin gpio pin handle to operate. + \param[in] mode \ref gpio_mode_e + \return error code +*/ +int32_t csi_gpio_pin_config_mode(gpio_pin_handle_t handle, + gpio_mode_e mode) +{ + GPIO_NULL_PARAM_CHK(handle); + + /* config the gpio pin mode direction mask bits */ + dw_gpio_pin_priv_t *gpio_pin_priv = handle; + + uint8_t offset = gpio_pin_priv->idx; + + int32_t ret = drv_pin_config_mode(gpio_pin_priv->portidx, offset, mode); + + return ret; +} +/** + \brief config pin direction + \param[in] pin gpio pin handle to operate. + \param[in] dir \ref gpio_direction_e + \return error code +*/ +int32_t csi_gpio_pin_config_direction(gpio_pin_handle_t handle, + gpio_direction_e dir) +{ + GPIO_NULL_PARAM_CHK(handle); + + /* config the gpio pin mode direction mask bits */ + dw_gpio_pin_priv_t *gpio_pin_priv = handle; + + /* convert portidx to port handle */ + dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx]; + + gpio_priv->dir = dir; + gpio_priv->mask = 1 << gpio_pin_priv->idx; + + uint32_t ret = gpio_set_direction(gpio_priv, dir); + + if (ret) + { + return ret; + } + + return 0; +} + +/** + \brief config pin + \param[in] handle gpio pin handle to operate. + \param[in] mode \ref gpio_mode_e + \param[in] dir \ref gpio_direction_e + \return error code +*/ +int32_t csi_gpio_pin_config(gpio_pin_handle_t handle, + gpio_mode_e mode, + gpio_direction_e dir) +{ + GPIO_NULL_PARAM_CHK(handle); + + /* config the gpio pin mode direction mask bits */ + dw_gpio_pin_priv_t *gpio_pin_priv = handle; + + /* convert portidx to port handle */ + dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx]; + + gpio_priv->mode = mode; + gpio_priv->dir = dir; + gpio_priv->mask = 1 << gpio_pin_priv->idx; + + uint32_t ret = gpio_set_direction(gpio_priv, dir); + + if (ret) + { + return ret; + } + + return 0; + +} + +/** + \brief Set one or zero to the selected GPIO pin. + \param[in] handle gpio pin handle to operate. + \param[in] value the value to be set + \return error code +*/ +int32_t csi_gpio_pin_write(gpio_pin_handle_t handle, bool value) +{ + GPIO_NULL_PARAM_CHK(handle); + + dw_gpio_pin_priv_t *gpio_pin_priv = handle; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + uint8_t offset = gpio_pin_priv->idx; + uint32_t port_value = value << offset; + + port_handle->value = port_value; + gpio_write(port_handle, (1 << offset)); + + return 0; + +} + +/** + \brief Get the value of selected GPIO pin. + \param[in] handle gpio pin handle to operate. + \param[out] value buf to store the pin value + \return error code +*/ +int32_t csi_gpio_pin_read(gpio_pin_handle_t handle, bool *value) +{ + GPIO_NULL_PARAM_CHK(handle); + GPIO_NULL_PARAM_CHK(value); + + dw_gpio_pin_priv_t *gpio_pin_priv = handle; + uint32_t port_value; + uint8_t offset = gpio_pin_priv->idx; + + /* convert portidx to port handle */ + dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx]; + + gpio_read(port_handle, &port_value); + *value = (port_value & (1 << offset)) >> offset; + + return 0; +} + +/** + \brief set GPIO interrupt mode. + \param[in] handle gpio pin handle to operate. + \param[in] mode the irq mode to be set + \param[in] enable the enable flag + \return error code +*/ +int32_t csi_gpio_pin_set_irq(gpio_pin_handle_t handle, gpio_irq_mode_e mode, bool enable) +{ + GPIO_NULL_PARAM_CHK(handle); + + uint32_t ret = 0; + + if (enable) + { + ret = gpio_set_irq_mode(handle, mode); + + if (ret) + { + return ret; + } + + gpio_irq_enable(handle); + + } else + { + gpio_irq_disable(handle); + + } + + return ret; +} + |
