/* * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file wm_gpio.c * * @brief GPIO Driver Module * * @author dave * * Copyright (c) 2014 Winner Microelectronics Co., Ltd. */ #include "wm_gpio.h" #include "wm_regs.h" #include "wm_irq.h" #include "wm_osal.h" #include "tls_common.h" struct gpio_irq_context { tls_gpio_irq_callback callback; void *arg; }; static struct gpio_irq_context gpio_context[WM_IO_PB_31 - WM_IO_PA_00 + 1] = {{0, 0}}; ATTRIBUTE_ISR void GPIOA_IRQHandler(void) { u8 i = 0; u8 found = 0; u32 reg = 0; csi_kernel_intrpt_enter(); reg = tls_reg_read32(HR_GPIO_MIS); for (i = 0; i <= WM_IO_PA_15; i++) { if (reg & BIT(i)) { found = 1; break; } } if (found) { if (gpio_context[i].callback != NULL) gpio_context[i].callback(gpio_context[i].arg); } csi_kernel_intrpt_exit(); } ATTRIBUTE_ISR void GPIOB_IRQHandler(void) { u8 i = 0; u8 found = 0; u32 reg = 0; csi_kernel_intrpt_enter(); reg = tls_reg_read32(HR_GPIO_MIS + TLS_IO_AB_OFFSET); for (i = WM_IO_PB_00; i <= WM_IO_PB_31; i++) { if (reg & BIT(i - WM_IO_PB_00)) { found = 1; break; } } if (found) { if (gpio_context[i].callback != NULL) gpio_context[i].callback(gpio_context[i].arg); } csi_kernel_intrpt_exit(); } /** * @brief This function is used to config gpio function * * @param[in] gpio_pin gpio pin num * @param[in] dir gpio direction * @param[in] attr gpio attribute * * @return None * * @note */ void tls_gpio_cfg(enum tls_io_name gpio_pin, enum tls_gpio_dir dir, enum tls_gpio_attr attr) { u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; } else { pin = gpio_pin; offset = 0; } /* enable gpio function */ tls_io_cfg_set(gpio_pin, WM_IO_OPT5_GPIO); /* gpio direction */ if (WM_GPIO_DIR_OUTPUT == dir) tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) | BIT(pin)); /* 1 set output */ else if (WM_GPIO_DIR_INPUT == dir) tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) & (~BIT(pin))); /* 0 set input */ /* gpio attribute */ if (WM_GPIO_ATTR_FLOATING == attr) { /* 1 disable pullup */ tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) | BIT(pin)); /* 1 disable pulldown */ tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset)&(~BIT(pin))); } if (WM_GPIO_ATTR_PULLHIGH == attr) { /* 0 enable pullup */ tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) & (~BIT(pin))); /* 0 disable pulldown */ tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset) &(~BIT(pin))); } if (WM_GPIO_ATTR_PULLLOW == attr) { /* 0 disable pullup */ tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) | BIT(pin)); /* 1 disable pulldown */ tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset) | BIT(pin)); } } /** * @brief This function is used to read gpio status * * @param[in] gpio_pin gpio pin num * * @retval 0 power level is low * @retval 1 power level is high * * @note None */ u8 tls_gpio_read(enum tls_io_name gpio_pin) { u32 reg_en; u32 reg; u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; } else { pin = gpio_pin; offset = 0; } reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset); tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin)); reg = tls_reg_read32(HR_GPIO_DATA + offset); tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en); if (reg & (0x1 << pin)) return 1; else return 0; } /** * @brief This function is used to modify gpio status * * @param[in] gpio_pin gpio pin num * @param[in] value power level * 0: low power level * 1: high power level * * @return None * * @note None */ void tls_gpio_write(enum tls_io_name gpio_pin, u8 value) { u32 cpu_sr = 0; u32 reg; u32 reg_en; u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; } else { pin = gpio_pin; offset = 0; } cpu_sr = tls_os_set_critical(); reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset); tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin)); reg = tls_reg_read32(HR_GPIO_DATA + offset); if (value) tls_reg_write32(HR_GPIO_DATA + offset, reg | (1 << pin)); /* write high */ else tls_reg_write32(HR_GPIO_DATA + offset, reg & (~(1 << pin))); /* write low */ tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en); tls_os_release_critical(cpu_sr); } /** * @brief This function is used to config gpio interrupt * * @param[in] gpio_pin gpio pin num * @param[in] mode interrupt trigger type * * @return None * * @note None */ void tls_gpio_irq_enable(enum tls_io_name gpio_pin, enum tls_gpio_irq_trig mode) { u32 reg; u8 pin; u16 offset; u8 vec_no; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; vec_no = GPIOB_IRQn; } else { pin = gpio_pin; offset = 0; vec_no = GPIOA_IRQn; } switch (mode) { case WM_GPIO_IRQ_TRIG_RISING_EDGE: reg = tls_reg_read32(HR_GPIO_IS + offset); reg &= (~(0x1 << pin)); // TLS_DBGPRT_INFO("\r\nrising edge is ret=%x\r\n",reg); tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */ reg = tls_reg_read32(HR_GPIO_IBE + offset); reg &= (~(0x1 << pin)); // TLS_DBGPRT_INFO("\r\nrising edge ibe ret=%x\r\n",reg); tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */ reg = tls_reg_read32(HR_GPIO_IEV + offset); reg |= (0x1 << pin); // TLS_DBGPRT_INFO("\r\nrising edge iev ret=%x\r\n",reg); tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 1 rising edge trigger */ break; case WM_GPIO_IRQ_TRIG_FALLING_EDGE: reg = tls_reg_read32(HR_GPIO_IS + offset); reg &= (~(0x1 << pin)); // TLS_DBGPRT_INFO("\falling edge is ret=%x\n",reg); tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */ reg = tls_reg_read32(HR_GPIO_IBE + offset); reg &= (~(0x1 << pin)); // TLS_DBGPRT_INFO("\falling edge ibe ret=%x\n",reg); tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */ reg = tls_reg_read32(HR_GPIO_IEV + offset); reg &= (~(0x1 << pin)); // TLS_DBGPRT_INFO("\falling edge iev ret=%x\n",reg); tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 0 falling edge trigger */ break; case WM_GPIO_IRQ_TRIG_DOUBLE_EDGE: reg = tls_reg_read32(HR_GPIO_IS + offset); tls_reg_write32(HR_GPIO_IS + offset, reg & (~(0x1 << pin))); /* 0 edge trigger */ reg = tls_reg_read32(HR_GPIO_IBE + offset); tls_reg_write32(HR_GPIO_IBE + offset, reg | (0x1 << pin)); /* 1 double edge trigger */ break; case WM_GPIO_IRQ_TRIG_HIGH_LEVEL: reg = tls_reg_read32(HR_GPIO_IS + offset); tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */ reg = tls_reg_read32(HR_GPIO_IEV + offset); tls_reg_write32(HR_GPIO_IEV + offset, reg | (0x1 << pin)); /* 1 high level trigger */ break; case WM_GPIO_IRQ_TRIG_LOW_LEVEL: reg = tls_reg_read32(HR_GPIO_IS + offset); tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */ reg = tls_reg_read32(HR_GPIO_IEV + offset); tls_reg_write32(HR_GPIO_IEV + offset, reg & (~(0x1 << pin))); /* 0 low level trigger */ break; } reg = tls_reg_read32(HR_GPIO_IE + offset); reg |= (0x1 << pin); tls_reg_write32(HR_GPIO_IE + offset, reg); /* enable interrupt */ tls_irq_enable(vec_no); } /** * @brief This function is used to disable gpio interrupt * * @param[in] gpio_pin gpio pin num * * @return None * * @note None */ void tls_gpio_irq_disable(enum tls_io_name gpio_pin) { u32 reg; u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; reg = tls_reg_read32(HR_GPIO_IE + offset); tls_reg_write32(HR_GPIO_IE + offset, reg & (~(0x1 << pin))); /* disable interrupt */ reg = reg&(~(0x1 << pin)); if (reg == 0) { tls_irq_disable(GPIOB_IRQn); } } else { pin = gpio_pin; offset = 0; reg = tls_reg_read32(HR_GPIO_IE + offset); tls_reg_write32(HR_GPIO_IE + offset, reg & (~(0x1 << pin))); /* disable interrupt */ reg = (reg&(~(0x1 << pin)))&0xFFFF; if (reg == 0) { tls_irq_disable(GPIOA_IRQn); } } } /** * @brief This function is used to get gpio interrupt status * * @param[in] gpio_pin gpio pin num * * @retval 0 no interrupt happened * @retval 1 interrupt happened * * @note None */ u8 tls_get_gpio_irq_status(enum tls_io_name gpio_pin) { u32 reg; u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; } else { pin = gpio_pin; offset = 0; } reg = tls_reg_read32(HR_GPIO_RIS + offset); if (reg & (0x1 << pin)) return 1; else return 0; } /** * @brief This function is used to clear gpio interrupt flag * * @param[in] gpio_pin gpio pin num * * @return None * * @note None */ void tls_clr_gpio_irq_status(enum tls_io_name gpio_pin) { u8 pin; u16 offset; if (gpio_pin >= WM_IO_PB_00) { pin = gpio_pin - WM_IO_PB_00; offset = TLS_IO_AB_OFFSET; } else { pin = gpio_pin; offset = 0; } tls_reg_write32(HR_GPIO_IC + offset, (0x1 << pin)); /* 1 clear interrupt status */ } /** * @brief This function is used to register gpio interrupt * * @param[in] gpio_pin gpio pin num * @param[in] callback the gpio interrupt call back function * @param[in] arg parammeter for the callback * * @return None * * @note * gpio callback function is called in interrupt, * so can not operate the critical data in the callback fuuction, * recommendation to send messages to other tasks to operate it. */ void tls_gpio_isr_register(enum tls_io_name gpio_pin, tls_gpio_irq_callback callback, void *arg) { gpio_context[gpio_pin].callback = callback; gpio_context[gpio_pin].arg = arg; }