Files
base/drivers/source/gpio/sdrv_gpio.c
2025-11-07 09:57:14 +08:00

1297 lines
36 KiB
C

/**
* @file sdrv_gpio.c
* @brief Sdrv GPIO driver.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <bits.h>
#include <debug.h>
#include <device_pin.h>
#include <irq.h>
#include <irq_num.h>
#include <param.h>
#include <part.h>
#include <reg.h>
#include <regs_base.h>
#include <sdrv_gpio.h>
#include <types.h>
#define REG_OEN_OFFSET (0x580u)
#define REG_DATA_IN_OFFSET (0x600u)
#define REG_DATA_OUT_OFFSET (0x680u)
#define REG_SINT_EN_OFFSET (0x700u)
#define REG_SINT_MASK_OFFSET (0x780u)
#define REG_SINT_TYPE_OFFSET (0x800u)
#define REG_SINT_POL_OFFSET (0x880u)
#define REG_SINT_BOE_OFFSET (0x900u)
#define REG_SINT_STATUS_OFFSET (0x980u)
#define REG_SINT_STATUS_UNMASK_OFFSET (0xa00u)
#define REG_SINT_EDGE_CLR_OFFSET (0xa80u)
#define REG_AINT_EN_OFFSET (0xb00u)
#define REG_AINT_MASK_OFFSET (0xb80u)
#define REG_AINT_TYPE_OFFSET (0xc00u)
#define REG_AINT_POL_OFFSET (0xc80u)
#define REG_AINT_BOE_OFFSET (0xd00u)
#define REG_AINT_STATUS_OFFSET (0xd80u)
#define REG_AINT_STATUS_UNMASK_OFFSET (0xe00u)
#define REG_AINT_EDGE_CLR_OFFSET (0xe80u)
#define REG_INT_SHARE(p) ((p)*0x4 + 0x1000)
/* GPIO controller register bits */
#define DIR_IN 0
#define DIR_OUT 1
#define INT_TYPE_LEVEL_SENSITIVE 0
#define INT_TYPE_EDGE_SENSITIVE 1
#define INT_POLARITY_LOW_OR_NEGATIVE 0
#define INT_POLARITY_HIGH_OR_POSITIVE 1
#define INT_BOTH_EDGE_SINGLE 0
#define INT_BOTH_EDGE_BOTH 1
#define PIN_OFFSET(pin) ((pin) / 32 * 0x10)
#define PIN_SET_OFFSET(pin) (PIN_OFFSET(pin) + 0x4)
#define PIN_CLR_OFFSET(pin) (PIN_OFFSET(pin) + 0x8)
#define PIN_TOG_OFFSET(pin) (PIN_OFFSET(pin) + 0xC)
#define PIN_BIT(pin) ((pin) % 32)
#define PIN_MASK(pin) (1ul << PIN_BIT(pin))
typedef struct sdrv_gpio_handler {
sdrv_gpio_interrupt_handler_t handler;
void *arg;
} sdrv_gpio_handler_t;
typedef struct sdrv_gpio_irq {
uint32_t irq;
uint32_t refs;
} sdrv_gpio_irq_t;
static sdrv_gpio_handler_t gpio_irq_handler[TAISHAN_PIN_NUM];
#if (CONFIG_E3L)
static sdrv_gpio_irq_t gpio_irq_info[4] = {
{GPIO_SF_SYNC_GRP_0_INTR_NUM, 0}, /*0*/
{GPIO_SF_SYNC_GRP_1_INTR_NUM, 0}, /*1*/
{GPIO_SF_SYNC_GRP_2_INTR_NUM, 0}, /*2*/
{GPIO_SF_SYNC_GRP_3_INTR_NUM, 0}, /*3*/
};
#else
static sdrv_gpio_irq_t gpio_irq_info[8] = {
{GPIO_SF_SYNC_GRP_0_INTR_NUM, 0}, /*0*/
{GPIO_SF_SYNC_GRP_1_INTR_NUM, 0}, /*1*/
{GPIO_SF_SYNC_GRP_2_INTR_NUM, 0}, /*2*/
{GPIO_SF_SYNC_GRP_3_INTR_NUM, 0}, /*3*/
{GPIO_SF_SYNC_GRP_4_INTR_NUM, 0}, /*4*/
{GPIO_AP_SYNC_GRP_0_INTR_NUM, 0}, /*5*/
{GPIO_AP_SYNC_GRP_1_INTR_NUM, 0}, /*6*/
{GPIO_AP_SYNC_GRP_2_INTR_NUM, 0}, /*7*/
};
#endif
static int32_t get_array_index(uint32_t pin_index)
{
int32_t array_index;
uint32_t ctrl_index;
array_index = 0;
ctrl_index = pin_index;
#if (CONFIG_E3 || CONFIG_D3)
if (ctrl_index >= TAISHAN_SAFETY_PIN_NUM) {
ctrl_index -= TAISHAN_SAFETY_PIN_NUM;
array_index = 5;
}
#endif
array_index += (ctrl_index / 32U);
return (array_index < ARRAY_SIZE(gpio_irq_info)) ? array_index : -1;
}
static int gpio_irq_common_handler(uint32_t irq, void *arg)
{
uint32_t array_index = (uint32_t)arg;
uint32_t irq_status;
uint32_t async_irq_status;
uint32_t pin_start;
uint32_t clear_reg;
if ((array_index >= ARRAY_SIZE(gpio_irq_info)) ||
(irq != gpio_irq_info[array_index].irq)) {
return SDRV_GPIO_BAD_PIN;
}
if (array_index < 5) {
irq_status = readl(APB_GPIO_SF_BASE + REG_SINT_STATUS_OFFSET +
array_index * 0x10);
async_irq_status = readl(APB_GPIO_SF_BASE + REG_AINT_STATUS_OFFSET +
array_index * 0x10);
pin_start = array_index * 32;
clear_reg = APB_GPIO_SF_BASE + array_index * 0x10 + 0x4;
}
#if (!CONFIG_E3L)
else {
array_index -= 5;
irq_status = readl(APB_GPIO_AP_BASE + REG_SINT_STATUS_OFFSET +
array_index * 0x10);
async_irq_status = readl(APB_GPIO_AP_BASE + REG_AINT_STATUS_OFFSET +
array_index * 0x10);
pin_start = array_index * 32 + TAISHAN_AP_PIN_START;
clear_reg = APB_GPIO_AP_BASE + array_index * 0x10 + 0x4;
}
#endif
for (uint32_t i = 0; i < 32; i++) {
if ((irq_status & (0x1u << i)) || (async_irq_status & (0x1u << i))) {
if (gpio_irq_handler[pin_start + i].handler) {
writel((0x1u << i), clear_reg + REG_SINT_EDGE_CLR_OFFSET);
writel((0x1u << i), clear_reg + REG_AINT_EDGE_CLR_OFFSET);
gpio_irq_handler[pin_start + i].handler(
irq, pin_start + i, gpio_irq_handler[pin_start + i].arg);
}
}
}
return 0;
}
static bool gpio_read(paddr_t base, uint32_t reg_offset, uint32_t pin)
{
uint32_t val = readl(base + reg_offset + PIN_OFFSET(pin));
return ((val & PIN_MASK(pin)) == 0) ? false : true;
}
static void gpio_write(paddr_t base, uint32_t reg_offset, uint32_t pin,
bool val)
{
(val == true)
? writel(PIN_MASK(pin), base + reg_offset + PIN_SET_OFFSET(pin))
: writel(PIN_MASK(pin), base + reg_offset + PIN_CLR_OFFSET(pin));
}
static void gpio_toggle(paddr_t base, uint32_t reg_offset, uint32_t pin)
{
writel(PIN_MASK(pin), base + reg_offset + PIN_TOG_OFFSET(pin));
}
static status_t do_sdrv_gpio_set_pin_interrupt(uint32_t ctrl_base, uint32_t ctrl_index, gpio_interrupt_e intr)
{
gpio_write(ctrl_base, REG_SINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_SINT_EN_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_SINT_EDGE_CLR_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_IN);
switch (intr) {
case GPIO_INTR_DISABLE:
break;
case GPIO_INTR_HIGH_LEVEL:
gpio_write(ctrl_base, REG_SINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_LEVEL_SENSITIVE);
gpio_write(ctrl_base, REG_SINT_POL_OFFSET, ctrl_index,
INT_POLARITY_HIGH_OR_POSITIVE);
break;
case GPIO_INTR_LOW_LEVEL:
gpio_write(ctrl_base, REG_SINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_LEVEL_SENSITIVE);
gpio_write(ctrl_base, REG_SINT_POL_OFFSET, ctrl_index,
INT_POLARITY_LOW_OR_NEGATIVE);
break;
case GPIO_INTR_RISING_EDGE:
gpio_write(ctrl_base, REG_SINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_SINGLE);
gpio_write(ctrl_base, REG_SINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
gpio_write(ctrl_base, REG_SINT_POL_OFFSET, ctrl_index,
INT_POLARITY_HIGH_OR_POSITIVE);
break;
case GPIO_INTR_FALLING_EDGE:
gpio_write(ctrl_base, REG_SINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_SINGLE);
gpio_write(ctrl_base, REG_SINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
gpio_write(ctrl_base, REG_SINT_POL_OFFSET, ctrl_index,
INT_POLARITY_LOW_OR_NEGATIVE);
break;
case GPIO_INTR_BOTH_EDGE:
gpio_write(ctrl_base, REG_SINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_BOTH);
gpio_write(ctrl_base, REG_SINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
break;
default:
return SDRV_STATUS_INVALID_PARAM;
}
return SDRV_STATUS_OK;
}
static status_t do_sdrv_gpio_set_pin_async_interrupt(uint32_t ctrl_base, uint32_t ctrl_index,
gpio_interrupt_e intr)
{
gpio_write(ctrl_base, REG_AINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_AINT_EN_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_AINT_EDGE_CLR_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_IN);
switch (intr) {
case GPIO_INTR_DISABLE:
break;
case GPIO_INTR_HIGH_LEVEL:
gpio_write(ctrl_base, REG_AINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_LEVEL_SENSITIVE);
gpio_write(ctrl_base, REG_AINT_POL_OFFSET, ctrl_index,
INT_POLARITY_HIGH_OR_POSITIVE);
break;
case GPIO_INTR_LOW_LEVEL:
gpio_write(ctrl_base, REG_AINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_LEVEL_SENSITIVE);
gpio_write(ctrl_base, REG_AINT_POL_OFFSET, ctrl_index,
INT_POLARITY_LOW_OR_NEGATIVE);
break;
case GPIO_INTR_RISING_EDGE:
gpio_write(ctrl_base, REG_AINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_SINGLE);
gpio_write(ctrl_base, REG_AINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
gpio_write(ctrl_base, REG_AINT_POL_OFFSET, ctrl_index,
INT_POLARITY_HIGH_OR_POSITIVE);
break;
case GPIO_INTR_FALLING_EDGE:
gpio_write(ctrl_base, REG_AINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_SINGLE);
gpio_write(ctrl_base, REG_AINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
gpio_write(ctrl_base, REG_AINT_POL_OFFSET, ctrl_index,
INT_POLARITY_LOW_OR_NEGATIVE);
break;
case GPIO_INTR_BOTH_EDGE:
gpio_write(ctrl_base, REG_AINT_BOE_OFFSET, ctrl_index,
INT_BOTH_EDGE_BOTH);
gpio_write(ctrl_base, REG_AINT_TYPE_OFFSET, ctrl_index,
INT_TYPE_EDGE_SENSITIVE);
break;
default:
return SDRV_STATUS_INVALID_PARAM;
}
return SDRV_STATUS_OK;
}
/**
* @brief Config gpio direction.
*
* This function config gpio as input or output.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] dir direction defined as gpio_direction_e.
*/
status_t sdrv_gpio_set_pin_direction(uint32_t pin_index, gpio_direction_e dir)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
if (dir == GPIO_DIR_OUT) {
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_OUT);
} else {
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_IN);
}
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Config gpio output level.
*
* This function config gpio output high or low level.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] level true represents high, false represents low.
*/
status_t sdrv_gpio_set_pin_output_level(uint32_t pin_index, bool level)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
if (level) {
gpio_write(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index, true);
} else {
gpio_write(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index, false);
}
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get gpio output level.
*
* This function read gpio output data register for specific pin.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents high configed, false represents low configed.
*/
bool sdrv_gpio_get_pin_output_level(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
ret = gpio_read(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index);
}
return ret;
}
/**
* @brief Toggle specific pin output level.
*
* This function toggle output level for specific pin, for example, if pin
* output high at first, after call this function, it will output low.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_toggle_pin_output_level(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_toggle(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin input level.
*
* This function read pin input level.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents high level, false represents low level.
*/
bool sdrv_gpio_read_pin_input_level(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
ret = gpio_read(ctrl_base, REG_DATA_IN_OFFSET, ctrl_index);
}
return ret;
}
/**
* @brief Config interrupt type for specific pin.
*
* This function config pin as interrupt type, after configuration, you also
* need call sdrv_gpio_pin_interrupt_enable for starting receive interrupt.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] intr interrupt type defined in gpio_interrupt_e.
*/
status_t sdrv_gpio_set_pin_interrupt(uint32_t pin_index, gpio_interrupt_e intr)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index >= TAISHAN_PIN_NUM) {
return SDRV_GPIO_BAD_PIN;
}
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
return do_sdrv_gpio_set_pin_interrupt(ctrl_base, ctrl_index, intr);
}
/**
* @brief Enable interrupt for specific pin.
*
* This function config pin interrupt enable register and unmask pin interrupt
* status.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_pin_interrupt_enable(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_SINT_MASK_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_SINT_EN_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Disable interrupt for specific pin.
*
* This function config pin interrupt disable register and mask pin interrupt
* status.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_pin_interrupt_disable(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_SINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_SINT_EN_OFFSET, ctrl_index, 0);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin interrupt status.
*
* This function read pin interrupt status register.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents interrupt status is set, false represents no
* interrupt status.
*/
bool sdrv_gpio_pin_interrupt_status(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
ret = gpio_read(ctrl_base, REG_SINT_STATUS_OFFSET, ctrl_index);
}
return ret;
}
/**
* @brief Clear specific pin interrupt status.
*
* This function clear pin interrupt status. Only edge interrupt type need clear
* status, level type should inactive source level.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_clear_pin_interrupt_status(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_SINT_EDGE_CLR_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Config asynchronous interrupt type for specific pin.
*
* This function config pin as interrupt type using asynchronous way. Compare to
* normal interrupt way, asynchronous interrupt can also receive interrupt
* without clock, so it can be work in lowpower mode.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] intr interrupt type defined in gpio_interrupt_e.
*/
status_t sdrv_gpio_set_pin_async_interrupt(uint32_t pin_index,
gpio_interrupt_e intr)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index >= TAISHAN_PIN_NUM) {
return SDRV_GPIO_BAD_PIN;
}
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
return do_sdrv_gpio_set_pin_async_interrupt(ctrl_base, ctrl_index, intr);
}
/**
* @brief Enable asynchronous interrupt for specific pin.
*
* This function config pin interrupt enable register and unmask pin interrupt
* status.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_pin_async_interrupt_enable(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_AINT_MASK_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_AINT_EN_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Disable asynchronous interrupt for specific pin.
*
* This function config pin interrupt disable register and mask pin interrupt
* status.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_pin_async_interrupt_disable(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_AINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_AINT_EN_OFFSET, ctrl_index, 0);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin asynchronous interrupt status.
*
* This function read pin asynchronous interrupt status register.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents interrupt status is set, false represents no
* interrupt status.
*/
bool sdrv_gpio_pin_async_interrupt_status(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
ret = gpio_read(ctrl_base, REG_AINT_STATUS_OFFSET, ctrl_index);
}
return ret;
}
/**
* @brief Clear specific pin asynchronous interrupt status.
*
* This function clear pin asynchronous interrupt status. Only edge interrupt
* type need clear status, level type should inactive source level.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_clear_pin_async_interrupt_status(uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
gpio_write(ctrl_base, REG_AINT_EDGE_CLR_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Register interrupt callback function for specific pin.
*
* This function register pin interrupt callback. It according to pin index
* getting irq number, and use irq_attach/irq_enable to enable it. Each 32 pin
* share one interrupt number.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] handler callback function handler.
* @param [in] arg callback function param.
*/
status_t sdrv_gpio_register_interrupt_handler(
uint32_t pin_index, sdrv_gpio_interrupt_handler_t handler, void *arg)
{
status_t ret;
irq_state_t irq_stat;
uint32_t refs;
int32_t array_index;
if (pin_index >= TAISHAN_PIN_NUM) {
return SDRV_GPIO_BAD_PIN;
}
array_index = get_array_index(pin_index);
if (array_index < 0) {
return SDRV_GPIO_BAD_PIN;
}
irq_stat = arch_irq_save();
gpio_irq_handler[pin_index].handler = handler;
gpio_irq_handler[pin_index].arg = arg;
refs = ++(gpio_irq_info[array_index].refs);
arch_irq_restore(irq_stat);
if (refs == 1) {
ret = irq_attach(gpio_irq_info[array_index].irq,
gpio_irq_common_handler, (void *)array_index);
if (ret == SDRV_STATUS_OK) {
irq_enable(gpio_irq_info[array_index].irq);
} else {
return SDRV_GPIO_IRQ_ATTACH_FAIL;
}
}
return SDRV_STATUS_OK;
}
/**
* @brief Unregister interrupt callback function for specific pin.
*
* This function unregister pin interrupt callback. If it is last pin for its
* group interrupt number, irq_disable/irq_detach will be called.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_unregister_interrupt_handler(uint32_t pin_index)
{
irq_state_t irq_stat;
uint32_t refs;
int32_t array_index;
if (pin_index >= TAISHAN_PIN_NUM) {
return SDRV_GPIO_BAD_PIN;
}
array_index = get_array_index(pin_index);
if (array_index < 0) {
return SDRV_GPIO_BAD_PIN;
}
irq_stat = arch_irq_save();
gpio_irq_handler[pin_index].handler = NULL;
gpio_irq_handler[pin_index].arg = NULL;
refs = --(gpio_irq_info[array_index].refs);
arch_irq_restore(irq_stat);
if (refs == 0) {
irq_disable(gpio_irq_info[array_index].irq);
irq_detach(gpio_irq_info[array_index].irq);
}
return SDRV_STATUS_OK;
}
/**
* @brief Set gpio interrupt share with other core.
*
* This function set gpio interrupt with core sp or sx. Pin interrupt default
* send to sf only, if you want use other core handler this interrupt, you must
* share interrupt with core.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] mask OR'ed value defined in gpio_interrupt_share_e.
*/
status_t sdrv_gpio_interrupt_share_with(uint32_t pin_index, uint32_t mask)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
uint32_t val;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
val = readl(ctrl_base + REG_INT_SHARE(ctrl_index));
val |= (mask & 0xffffu);
writel(val, ctrl_base + REG_INT_SHARE(ctrl_index));
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Cancel gpio interrupt share with other core.
*
* This function cancel gpio interrupt with core sp or sx.
*
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] mask OR'ed value defined in gpio_interrupt_share_e.
*/
status_t sdrv_gpio_cancel_interrupt_share_with(uint32_t pin_index,
uint32_t mask)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
uint32_t val;
if (pin_index < TAISHAN_PIN_NUM) {
ctrl_base = TAISHAN_GPIO_GET_BASE(pin_index);
ctrl_index = TAISHAN_GPIO_GET_INDEX(pin_index);
val = readl(ctrl_base + REG_INT_SHARE(ctrl_index));
val &= ~(mask & 0xffffu);
writel(val, ctrl_base + REG_INT_SHARE(ctrl_index));
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Config gpio direction.
*
* This function config gpio as input or output.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] dir direction defined as gpio_direction_e.
*/
status_t sdrv_gpio_ctrl_set_pin_direction(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index,
gpio_direction_e dir)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
if (dir == GPIO_DIR_OUT) {
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_OUT);
} else {
gpio_write(ctrl_base, REG_OEN_OFFSET, ctrl_index, DIR_IN);
}
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Config gpio output level.
*
* This function config gpio output high or low level.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] level true represents high, false represents low.
*/
status_t sdrv_gpio_ctrl_set_pin_output_level(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index, bool level)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
if (level) {
gpio_write(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index, true);
} else {
gpio_write(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index, false);
}
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Toggle specific pin output level.
*
* This function toggle output level for specific pin, for example, if pin
* output high at first, after call this function, it will output low.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_toggle_pin_output_level(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_toggle(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin input level.
*
* This function read pin input level.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents high level, false represents low level.
*/
bool sdrv_gpio_ctrl_read_pin_input_level(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (gpio_ctrl) {
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
ret = gpio_read(ctrl_base, REG_DATA_IN_OFFSET, ctrl_index);
}
}
return ret;
}
/**
* @brief Get gpio output level.
*
* This function read gpio output data register for specific pin.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents high configed, false represents low configed.
*/
bool sdrv_gpio_ctrl_get_pin_output_level(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (gpio_ctrl) {
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
ret = gpio_read(ctrl_base, REG_DATA_OUT_OFFSET, ctrl_index);
}
}
return ret;
}
/**
* @brief Config interrupt type for specific pin.
*
* This function config pin as interrupt type, after configuration, you also
* need call sdrv_gpio_pin_interrupt_enable for starting receive interrupt.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] intr interrupt type defined in gpio_interrupt_e.
*/
status_t sdrv_gpio_ctrl_set_pin_interrupt(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index,
gpio_interrupt_e intr)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
return do_sdrv_gpio_set_pin_interrupt(ctrl_base, ctrl_index, intr);
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Enable interrupt for specific pin.
*
* This function config pin interrupt enable register and unmask pin interrupt
* status.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_pin_interrupt_enable(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_SINT_MASK_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_SINT_EN_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Disable interrupt for specific pin.
*
* This function config pin interrupt disable register and mask pin interrupt
* status.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_pin_interrupt_disable(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_SINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_SINT_EN_OFFSET, ctrl_index, 0);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin interrupt status.
*
* This function read pin interrupt status register.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents interrupt status is set, false represents no
* interrupt status.
*/
bool sdrv_gpio_ctrl_pin_interrupt_status(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (gpio_ctrl) {
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
ret = gpio_read(ctrl_base, REG_SINT_STATUS_OFFSET, ctrl_index);
}
}
return ret;
}
/**
* @brief Clear specific pin interrupt status.
*
* This function clear pin interrupt status. Only edge interrupt type need clear
* status, level type should inactive source level.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_clear_pin_interrupt_status(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_SINT_EDGE_CLR_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Config asynchronous interrupt type for specific pin.
*
* This function config pin as interrupt type using asynchronous way. Compare to
* normal interrupt way, asynchronous interrupt can also receive interrupt
* without clock, so it can be work in lowpower mode.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @param [in] intr interrupt type defined in gpio_interrupt_e.
*/
status_t sdrv_gpio_ctrl_set_pin_async_interrupt(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index,
gpio_interrupt_e intr)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
return do_sdrv_gpio_set_pin_async_interrupt(ctrl_base, ctrl_index, intr);
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Enable asynchronous interrupt for specific pin.
*
* This function config pin interrupt enable register and unmask pin interrupt
* status.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_pin_async_interrupt_enable(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_AINT_MASK_OFFSET, ctrl_index, 0);
gpio_write(ctrl_base, REG_AINT_EN_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Disable asynchronous interrupt for specific pin.
*
* This function config pin interrupt disable register and mask pin interrupt
* status.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_pin_async_interrupt_disable(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_AINT_MASK_OFFSET, ctrl_index, 1);
gpio_write(ctrl_base, REG_AINT_EN_OFFSET, ctrl_index, 0);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}
/**
* @brief Get specific pin asynchronous interrupt status.
*
* This function read pin asynchronous interrupt status register.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
* @return true represents interrupt status is set, false represents no
* interrupt status.
*/
bool sdrv_gpio_ctrl_pin_async_interrupt_status(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
bool ret = false;
if (gpio_ctrl) {
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
ret = gpio_read(ctrl_base, REG_AINT_STATUS_OFFSET, ctrl_index);
}
}
return ret;
}
/**
* @brief Clear specific pin asynchronous interrupt status.
*
* This function clear pin asynchronous interrupt status. Only edge interrupt
* type need clear status, level type should inactive source level.
*
* @param [in] gpio_ctrl gpio instance.
* @param [in] pin_index defined pin macro in pinctrl.h.
*/
status_t sdrv_gpio_ctrl_clear_pin_async_interrupt_status(sdrv_gpio_t *gpio_ctrl,
uint32_t pin_index)
{
uint32_t ctrl_base;
uint32_t ctrl_index;
if (!gpio_ctrl) {
return SDRV_GPIO_CTRL_NULL;
}
ctrl_base = gpio_ctrl->base;
ctrl_index = pin_index - gpio_ctrl->pin_start;
if (ctrl_index < gpio_ctrl->pin_num) {
gpio_write(ctrl_base, REG_AINT_EDGE_CLR_OFFSET, ctrl_index, 1);
return SDRV_STATUS_OK;
} else {
return SDRV_GPIO_BAD_PIN;
}
}