/* ------------------------------------------ | |
* 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; i < max_int_bit_count; i++) | |
{ | |
if (gpio_bit_isr_state & (1 << i)) | |
{ | |
/* this bit interrupt enabled */ | |
if (port->gpio_bit_isr->int_bit_handler_ptr[i]) | |
{ | |
port->gpio_bit_isr->int_bit_handler_ptr[i](gpio_obj); | |
} | |
dw_gpio_int_clear(port, (1 << i)); /** clear this bit interrupt */ | |
} | |
} | |
error_exit: | |
return ercd; | |
} |