/* ------------------------------------------ * Copyright (c) 2016, Synopsys, Inc. All rights reserved. * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1) Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * 3) Neither the name of the Synopsys, Inc., nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * \version 2016.05 * \date 2014-07-22 * \author Wayne Ren(Wei.Ren@synopsys.com) --------------------------------------------- */ /** * \defgroup DEVICE_DW_GPIO Designware GPIO Driver * \ingroup DEVICE_DW * \brief Designware GPIO Driver Implementation */ /** * \file * \brief designware gpio driver * \ingroup DEVICE_DW_GPIO * \brief Designware GPIO driver */ #include "inc/embARC_toolchain.h" #include "inc/embARC_error.h" #include "inc/arc/arc_exception.h" #include "device/designware/gpio/dw_gpio.h" /** check expressions used in DesignWare GPIO driver implementation */ #define DW_GPIO_CHECK_EXP(EXPR, ERROR_CODE) CHECK_EXP(EXPR, ercd, ERROR_CODE, error_exit) #ifndef DISABLE_DEVICE_OBJECT_VALID_CHECK /** valid check of uart info object */ #define VALID_CHK_GPIO_INFO_OBJECT(gpioinfo_obj_ptr) { \ DW_GPIO_CHECK_EXP((gpioinfo_obj_ptr)!=NULL, E_OBJ); \ DW_GPIO_CHECK_EXP(((gpioinfo_obj_ptr)->gpio_ctrl)!=NULL, E_OBJ); \ } #endif /** * \defgroup DEVICE_DW_GPIO_STATIC DesignWare GPIO Driver Static Functions * \ingroup DEVICE_DW_GPIO * \brief Static or inline functions, variables for DesignWare GPIO handle gpio operations, * only used in this file * @{ */ Inline uint32_t dw_gpio_read_ext(DW_GPIO_PORT_PTR port) { return port->regs->EXT_PORTS[port->no]; } Inline uint32_t dw_gpio_read_dir(DW_GPIO_PORT_PTR port) { return port->regs->SWPORTS[port->no].DDR; } Inline uint32_t dw_gpio_read_dr(DW_GPIO_PORT_PTR port) { return port->regs->SWPORTS[port->no].DR; } Inline uint32_t dw_gpio_read_mthd(DW_GPIO_PORT_PTR port) { return port->regs->INTEN; } Inline void dw_gpio_int_enable(DW_GPIO_PORT_PTR port, uint32_t bit_mask) { port->regs->INTEN |= bit_mask; } Inline void dw_gpio_int_disable(DW_GPIO_PORT_PTR port, uint32_t bit_mask) { port->regs->INTEN &= (~bit_mask); } Inline void dw_gpio_int_mask(DW_GPIO_PORT_PTR port, uint32_t bit_mask) { port->regs->INTMASK |= bit_mask; } Inline void dw_gpio_int_unmask(DW_GPIO_PORT_PTR port, uint32_t bit_mask) { port->regs->INTMASK &= (~bit_mask); } Inline uint32_t dw_gpio_int_read_level(DW_GPIO_PORT_PTR port) { return port->regs->INTTYPE_LEVEL; } Inline uint32_t dw_gpio_int_read_polarity(DW_GPIO_PORT_PTR port) { return port->regs->INT_POLARITY; } Inline uint32_t dw_gpio_int_read_debounce(DW_GPIO_PORT_PTR port) { return port->regs->DEBOUNCE; } Inline uint32_t dw_gpio_int_read_status(DW_GPIO_PORT_PTR port) { return port->regs->INTSTATUS; } Inline void dw_gpio_int_clear(DW_GPIO_PORT_PTR port, uint32_t bit_mask) { port->regs->PORTA_EOI = bit_mask; } static void dw_gpio_int_write_level(DW_GPIO_PORT_PTR port, uint32_t bit_mask, uint32_t bit_level) { uint32_t reg_val; reg_val = port->regs->INTTYPE_LEVEL; reg_val &= (~bit_mask); bit_level &= bit_mask; reg_val |= bit_level; port->regs->INTTYPE_LEVEL = reg_val; } static void dw_gpio_int_write_polarity(DW_GPIO_PORT_PTR port, uint32_t bit_mask, uint32_t bit_polarity) { uint32_t reg_val; reg_val = port->regs->INT_POLARITY; reg_val &= (~bit_mask); bit_polarity &= bit_mask; reg_val |= bit_polarity; port->regs->INT_POLARITY = reg_val; } static void dw_gpio_int_write_debounce(DW_GPIO_PORT_PTR port, uint32_t bit_mask, uint32_t bit_debounce) { uint32_t reg_val; reg_val = port->regs->DEBOUNCE; reg_val &= (~bit_mask); bit_debounce &= bit_mask; reg_val |= bit_debounce; port->regs->DEBOUNCE = reg_val; } static void dw_gpio_set_int_cfg(DW_GPIO_PORT_PTR port, DEV_GPIO_INT_CFG *int_cfg) { dw_gpio_int_write_level(port, int_cfg->int_bit_mask, int_cfg->int_bit_type); dw_gpio_int_write_polarity(port, int_cfg->int_bit_mask, int_cfg->int_bit_polarity); dw_gpio_int_write_debounce(port, int_cfg->int_bit_mask, int_cfg->int_bit_debounce); } static void dw_gpio_get_int_cfg(DW_GPIO_PORT_PTR port, DEV_GPIO_INT_CFG *int_cfg) { int_cfg->int_bit_type = dw_gpio_int_read_level(port) & int_cfg->int_bit_mask; int_cfg->int_bit_polarity = dw_gpio_int_read_polarity(port) & int_cfg->int_bit_mask; int_cfg->int_bit_debounce = dw_gpio_int_read_debounce(port) & int_cfg->int_bit_mask; } static void dw_gpio_write_dr(DW_GPIO_PORT_PTR port, uint32_t bit_mask, uint32_t val) { uint32_t temp_reg; temp_reg = port->regs->SWPORTS[port->no].DR; temp_reg &= ~bit_mask; val &= bit_mask; temp_reg |= val; port->regs->SWPORTS[port->no].DR = temp_reg; } static void dw_gpio_write_dir(DW_GPIO_PORT_PTR port, uint32_t bit_mask, uint32_t val) { uint32_t temp_reg; temp_reg = port->regs->SWPORTS[port->no].DDR; temp_reg &= ~bit_mask; val &= bit_mask; temp_reg |= val; port->regs->SWPORTS[port->no].DDR = temp_reg; } static uint32_t dw_gpio_read_val(DW_GPIO_PORT_PTR port) { uint32_t val; val = dw_gpio_read_ext(port) & (~dw_gpio_read_dir(port)); val |= dw_gpio_read_dr(port) & dw_gpio_read_dir(port); return val; } /** @} end of group DEVICE_DW_GPIO_STATIC */ /* interface for DEV_GPIO */ /** Open designware gpio device with specified io direction configuration */ int32_t dw_gpio_open(DEV_GPIO *gpio_obj, uint32_t dir) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no <= DW_GPIO_PORT_D, E_OBJ); port_info_ptr->opn_cnt ++; if (port_info_ptr->opn_cnt > 1) { /* opened before */ if (dir == port_info_ptr->direction) { /* direction is the same */ return E_OK; } else { /* open with different direction */ return E_OPNED; } } dw_gpio_write_dir(port, port->valid_bit_mask, dir); if (port->no == DW_GPIO_PORT_A) { dw_gpio_int_clear(port, DW_GPIO_MASK_ALL); dw_gpio_int_disable(port, DW_GPIO_MASK_ALL); dw_gpio_int_unmask(port, DW_GPIO_MASK_ALL); /* install gpio interrupt handler */ int_handler_install(port->intno, port->int_handler); int_disable(port->intno); /** Set int type, int polarity and debounce configuration to default settings of device gpio */ dw_gpio_set_int_cfg(port, (DEV_GPIO_INT_CFG *)(&gpio_int_cfg_default)); port_info_ptr->method = dw_gpio_read_mthd(port); } else { port_info_ptr->method = DEV_GPIO_BITS_MTHD_DEFAULT; } dw_gpio_write_dr(port, port->valid_bit_mask, 0); port_info_ptr->direction = dir; port_info_ptr->extra = NULL; error_exit: return ercd; } /** Close designware gpio device */ int32_t dw_gpio_close(DEV_GPIO *gpio_obj) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no <= DW_GPIO_PORT_D, E_OBJ); DW_GPIO_CHECK_EXP(port_info_ptr->opn_cnt > 0, E_OK); port_info_ptr->opn_cnt --; if (port_info_ptr->opn_cnt == 0) { dw_gpio_write_dr(port, port->valid_bit_mask, 0); dw_gpio_write_dir(port, port->valid_bit_mask, 0); if (port->no == DW_GPIO_PORT_A) { dw_gpio_int_clear(port, DW_GPIO_MASK_ALL); dw_gpio_int_disable(port, DW_GPIO_MASK_ALL); int_disable(port->intno); } port_info_ptr->direction = 0; port_info_ptr->method = 0; port_info_ptr->extra = NULL; } else { ercd = E_OPNED; } error_exit: return ercd; } /** Read designware gpio device value */ int32_t dw_gpio_read(DEV_GPIO *gpio_obj, uint32_t *val, uint32_t mask) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no <= DW_GPIO_PORT_D, E_OBJ); DW_GPIO_CHECK_EXP(port_info_ptr->opn_cnt > 0, E_CLSED); DW_GPIO_CHECK_EXP(val!=NULL, E_PAR); //*val = dw_gpio_read_ext(port) & mask; *val = dw_gpio_read_val(port) & mask; error_exit: return ercd; } /** Write designware gpio device value */ int32_t dw_gpio_write(DEV_GPIO *gpio_obj, uint32_t val, uint32_t mask) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no <= DW_GPIO_PORT_D, E_OBJ); DW_GPIO_CHECK_EXP(port_info_ptr->opn_cnt > 0, E_CLSED); dw_gpio_write_dr(port, mask, val); error_exit: return ercd; } /** Control designware gpio device */ int32_t dw_gpio_control(DEV_GPIO *gpio_obj, uint32_t ctrl_cmd, void *param) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no <= DW_GPIO_PORT_D, E_OBJ); DW_GPIO_CHECK_EXP(port_info_ptr->opn_cnt > 0, E_CLSED); uint32_t val32; /** to receive unsigned int value */ if (ctrl_cmd == GPIO_CMD_SET_BIT_DIR_INPUT) { val32 = (uint32_t)param; dw_gpio_write_dir(port, val32, DW_GPIO_INPUT_ALL); port_info_ptr->direction = dw_gpio_read_dir(port); } else if (ctrl_cmd == GPIO_CMD_SET_BIT_DIR_OUTPUT) { val32 = (uint32_t)param; dw_gpio_write_dir(port, val32, DW_GPIO_OUTPUT_ALL); port_info_ptr->direction = dw_gpio_read_dir(port); } else if (ctrl_cmd == GPIO_CMD_GET_BIT_DIR) { DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); port_info_ptr->direction = dw_gpio_read_dir(port); *((int32_t *)param) = port_info_ptr->direction; } else { DW_GPIO_CHECK_EXP(port->no == DW_GPIO_PORT_A, E_NOSPT); /* output pin cannot be used as interrupt */ DEV_GPIO_INT_CFG *gpio_int_cfg; DEV_GPIO_BIT_ISR *port_bit_isr; switch (ctrl_cmd) { case GPIO_CMD_SET_BIT_INT_CFG: DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); gpio_int_cfg = (DEV_GPIO_INT_CFG *)param; dw_gpio_set_int_cfg(port, gpio_int_cfg); break; case GPIO_CMD_GET_BIT_INT_CFG: DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); gpio_int_cfg = (DEV_GPIO_INT_CFG *)param; /** read configuration, each bit stands for different configuration */ dw_gpio_get_int_cfg(port, gpio_int_cfg); break; case GPIO_CMD_SET_BIT_ISR: DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); port_bit_isr = (DEV_GPIO_BIT_ISR *)param; if (port_bit_isr->int_bit_ofs < port->gpio_bit_isr->int_bit_max_cnt) { port->gpio_bit_isr->int_bit_handler_ptr[port_bit_isr->int_bit_ofs] = port_bit_isr->int_bit_handler; } else { ercd = E_PAR; } break; case GPIO_CMD_GET_BIT_ISR: DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); port_bit_isr = (DEV_GPIO_BIT_ISR *)param; if (port_bit_isr->int_bit_ofs < port->gpio_bit_isr->int_bit_max_cnt) { port_bit_isr->int_bit_handler = port->gpio_bit_isr->int_bit_handler_ptr[port_bit_isr->int_bit_ofs]; } else { ercd = E_PAR; } break; case GPIO_CMD_ENA_BIT_INT: val32 = (uint32_t)param; dw_gpio_int_enable(port, val32); port_info_ptr->method = dw_gpio_read_mthd(port); if (port_info_ptr->method) { int_enable(port->intno); } break; case GPIO_CMD_DIS_BIT_INT: val32 = (uint32_t)param; dw_gpio_int_disable(port, val32); port_info_ptr->method = dw_gpio_read_mthd(port); if (port_info_ptr->method == 0) { int_disable(port->intno); } break; case GPIO_CMD_GET_BIT_MTHD: DW_GPIO_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); port_info_ptr->method = dw_gpio_read_mthd(port); *((int32_t *)param) = port_info_ptr->method; break; default: ercd = E_NOSPT; break; } } error_exit: return ercd; } /** designware gpio interrupt process */ int32_t dw_gpio_isr_handler(DEV_GPIO *gpio_obj, void *ptr) { int32_t ercd = E_OK; DEV_GPIO_INFO_PTR port_info_ptr = &(gpio_obj->gpio_info); /* START ERROR CHECK */ VALID_CHK_GPIO_INFO_OBJECT(port_info_ptr); /* END OF ERROR CHECK */ DW_GPIO_PORT_PTR port = (DW_GPIO_PORT_PTR)(port_info_ptr->gpio_ctrl); DW_GPIO_CHECK_EXP(port->no == DW_GPIO_PORT_A, E_NOSPT); uint32_t i, gpio_bit_isr_state; uint32_t max_int_bit_count = 0; /** read interrupt status */ gpio_bit_isr_state = dw_gpio_int_read_status(port); if (port->gpio_bit_isr) { max_int_bit_count = (port->gpio_bit_isr->int_bit_max_cnt); } else { dw_gpio_int_clear(port, gpio_bit_isr_state); } for (i=0; igpio_bit_isr->int_bit_handler_ptr[i]) { port->gpio_bit_isr->int_bit_handler_ptr[i](gpio_obj); } dw_gpio_int_clear(port, (1<