482 lines
14 KiB
C
482 lines
14 KiB
C
/* ------------------------------------------
|
|
* 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;
|
|
}
|