/** * @file sdrv_gpio.c * @brief Sdrv GPIO driver. * * @copyright Copyright (c) 2022 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #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; } }