rt-thread-official/bsp/synopsys/embarc/device/designware/iic/dw_iic.c

1585 lines
49 KiB
C

/* ------------------------------------------
* Copyright (c) 2017, 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 2017.03
* \date 2014-06-30
* \author Huaqi Fang(Huaqi.Fang@synopsys.com)
--------------------------------------------- */
/**
* \defgroup DEVICE_DW_IIC Designware IIC Driver
* \ingroup DEVICE_DW
* \brief Designware IIC Driver Implementation
*/
/**
* \file
* \brief Designware iic driver
* \ingroup DEVICE_DW_IIC
*/
#include <string.h>
#include "inc/embARC_toolchain.h"
#include "inc/embARC_error.h"
#include "inc/arc/arc_exception.h"
#include "device/designware/iic/dw_iic_hal.h"
#include "device/designware/iic/dw_iic.h"
/** check expressions used in DesignWare IIC driver implementation */
#define DW_IIC_CHECK_EXP(EXPR, ERROR_CODE) CHECK_EXP(EXPR, ercd, ERROR_CODE, error_exit)
#ifndef DISABLE_DEVICE_OBJECT_VALID_CHECK
/** valid check of iic info object */
#define VALID_CHK_IIC_INFO_OBJECT(iicinfo_obj_ptr) { \
DW_IIC_CHECK_EXP((iicinfo_obj_ptr)!=NULL, E_OBJ); \
DW_IIC_CHECK_EXP(((iicinfo_obj_ptr)->iic_ctrl)!=NULL, E_OBJ); \
}
#endif
/**
* \name DesignWare IIC Interrupt Callback Routine Select Marcos
* \brief DesignWare IIC interrupt callback routines select macros definitions
* @{
*/
#define DW_IIC_RDY_SND (1U) /*!< ready to send callback */
#define DW_IIC_RDY_RCV (2U) /*!< ready to receive callback */
/** @} */
/**
* \defgroup DEVICE_DW_IIC_STATIC DesignWare IIC Driver Static Functions
* \ingroup DEVICE_DW_IIC
* \brief Static or inline functions, variables for DesignWare IIC handle iic operations,
* only used in this file.
* @{
*/
/** Disable designware iic device */
Inline void dw_iic_disable(DW_IIC_REG *iic_reg_ptr)
{
iic_reg_ptr->IC_ENABLE = DW_IIC_DISABLE;
}
/** Enable designware iic device */
Inline void dw_iic_enable(DW_IIC_REG *iic_reg_ptr)
{
iic_reg_ptr->IC_ENABLE = DW_IIC_ENABLE;
}
/** Clear all designware iic interrupt */
Inline void dw_iic_clear_interrupt_all(DW_IIC_REG *iic_reg_ptr)
{
(void)iic_reg_ptr->IC_CLR_INTR;
}
/** test whether iic is ready to write, 1 ready, 0 not ready */
Inline int32_t dw_iic_putready(DW_IIC_REG *iic_reg_ptr)
{
return ((iic_reg_ptr->IC_STATUS & IC_STATUS_TFNF) != 0);
}
/** test whether iic is ready to receive, 1 ready, 0 not ready */
Inline int32_t dw_iic_getready(DW_IIC_REG *iic_reg_ptr)
{
return ((iic_reg_ptr->IC_STATUS & IC_STATUS_RFNE) != 0);
}
/** Write data into IIC TX FIFO with STOP/RESTART Condition, and R/W bit */
Inline void dw_iic_putdata(DW_IIC_REG *iic_reg_ptr, uint32_t data)
{
iic_reg_ptr->IC_DATA_CMD = data;
}
/** Read Data from IIC RX FIFO */
Inline uint32_t dw_iic_getdata(DW_IIC_REG *iic_reg_ptr)
{
return (iic_reg_ptr->IC_DATA_CMD) & 0xff;
}
/** Enable designware iic bit interrupt with mask */
Inline void dw_iic_unmask_interrupt(DW_IIC_REG *iic_reg_ptr, uint32_t mask)
{
iic_reg_ptr->IC_INTR_MASK |= mask;
}
/** Disable designware iic bit interrupt with mask */
Inline void dw_iic_mask_interrupt(DW_IIC_REG *iic_reg_ptr, uint32_t mask)
{
iic_reg_ptr->IC_INTR_MASK &= ~mask;
}
/** Get TX FIFO Length */
Inline uint32_t dw_iic_get_txfifo_len(DW_IIC_REG *iic_reg_ptr)
{
uint32_t txfifolen;
txfifolen = ((iic_reg_ptr->IC_COMP_PARAM_1 >> 16) & 0xff) + 1;
return txfifolen;
}
/** Get RX FIFO Length */
Inline uint32_t dw_iic_get_rxfifo_len(DW_IIC_REG *iic_reg_ptr)
{
uint32_t rxfifolen;
rxfifolen = ((iic_reg_ptr->IC_COMP_PARAM_1 >> 8) & 0xff) + 1;
return rxfifolen;
}
/** Set designware iic transfer in 7bit of 10bit addressing mode as a master */
Inline void dw_iic_set_mstaddr_mode(DW_IIC_REG *iic_reg_ptr, uint32_t mode)
{
#if DW_IIC_DYNAMIC_TAR_UPDATE_SUPPORT
if (mode == IIC_7BIT_ADDRESS) {
iic_reg_ptr->IC_TAR &= ~IC_10BITADDR_MASTER;
} else {
iic_reg_ptr->IC_TAR |= IC_10BITADDR_MASTER;
}
#else
dw_iic_disable(iic_reg_ptr);
if (mode == IIC_7BIT_ADDRESS) {
iic_reg_ptr->IC_CON &= ~MST_10_BIT_ADDR_MODE;
} else {
iic_reg_ptr->IC_CON |= MST_10_BIT_ADDR_MODE;
}
dw_iic_enable(iic_reg_ptr);
#endif
}
/** Set designware iic transfer in 7bit of 10bit addressing mode as a slave */
Inline void dw_iic_set_slvaddr_mode(DW_IIC_REG *iic_reg_ptr, uint32_t mode)
{
dw_iic_disable(iic_reg_ptr);
if (mode == IIC_7BIT_ADDRESS) {
iic_reg_ptr->IC_CON &= ~SLV_10_BIT_ADDR_MODE;
} else {
iic_reg_ptr->IC_CON |= SLV_10_BIT_ADDR_MODE;
}
dw_iic_enable(iic_reg_ptr);
}
/** Set designware iic transfer target address for addressing any iic slave device as a master */
Inline void dw_iic_set_taraddr(DW_IIC_REG *iic_reg_ptr, uint32_t address)
{
#if DW_IIC_DYNAMIC_TAR_UPDATE_SUPPORT
iic_reg_ptr->IC_TAR &= ~(IC_TAR_10BIT_ADDR_MASK);
iic_reg_ptr->IC_TAR |= (IC_TAR_10BIT_ADDR_MASK & address);
#else
dw_iic_disable(iic_reg_ptr);
iic_reg_ptr->IC_TAR &= ~(IC_TAR_10BIT_ADDR_MASK);
iic_reg_ptr->IC_TAR |= (IC_TAR_10BIT_ADDR_MASK & address);
dw_iic_enable(iic_reg_ptr);
#endif
}
/** Set designware iic slave address as a slave */
Inline void dw_iic_set_slvaddr(DW_IIC_REG *iic_reg_ptr, uint32_t address)
{
dw_iic_disable(iic_reg_ptr);
iic_reg_ptr->IC_SAR &= ~(IC_SAR_10BIT_ADDR_MASK);
iic_reg_ptr->IC_SAR |= (IC_SAR_10BIT_ADDR_MASK & address);
dw_iic_enable(iic_reg_ptr);
}
/** Select speed mode, and return proper speed mode configuration */
Inline uint32_t dw_iic_select_speedmode(uint32_t speedmode)
{
uint32_t speedcfg;
if (speedmode == IIC_SPEED_STANDARD) {
speedcfg = IC_CON_SPEED_STANDARD;
} else if (speedmode == IIC_SPEED_FAST) {
speedcfg = IC_CON_SPEED_FAST;
} else if (speedmode == IIC_SPEED_FASTPLUS) {
speedcfg = IC_CON_SPEED_FAST;
} else if (speedmode == IIC_SPEED_HIGH) {
speedcfg = IC_CON_SPEED_HIGH;
} else {
speedcfg = IC_CON_SPEED_HIGH;
}
return speedcfg;
}
/** Set designware iic speed mode */
Inline void dw_iic_set_speedmode(DW_IIC_REG *iic_reg_ptr, uint32_t speedmode)
{
uint32_t ic_con_val;
dw_iic_disable(iic_reg_ptr);
ic_con_val = iic_reg_ptr->IC_CON & (~IC_CON_SPEED_MASK);
ic_con_val |= dw_iic_select_speedmode(speedmode);
iic_reg_ptr->IC_CON = ic_con_val;
dw_iic_enable(iic_reg_ptr);
}
/** Set designware working mode as master or slave */
Inline void dw_iic_set_working_mode(DW_IIC_REG *iic_reg_ptr, uint32_t mode)
{
uint32_t ic_con_val;
dw_iic_disable(iic_reg_ptr);
ic_con_val = iic_reg_ptr->IC_CON & (~IC_CON_MST_SLV_MODE_MASK);
if (mode == DEV_MASTER_MODE) {
ic_con_val |= IC_CON_ENA_MASTER_MODE;
} else {
ic_con_val |= IC_CON_ENA_SLAVE_MODE;
}
dw_iic_enable(iic_reg_ptr);
}
/** Set IC_CLK frequency by configuration the *CNT registers for different speed modes */
Inline void dw_iic_set_scl_cnt(DW_IIC_REG *iic_reg_ptr, DW_IIC_SCL_CNT *scl_cnt)
{
dw_iic_disable(iic_reg_ptr);
iic_reg_ptr->IC_SS_SCL_HCNT = scl_cnt->ss_scl_hcnt;
iic_reg_ptr->IC_SS_SCL_LCNT = scl_cnt->ss_scl_lcnt;
iic_reg_ptr->IC_FS_SCL_HCNT = scl_cnt->fs_scl_hcnt;
iic_reg_ptr->IC_FS_SCL_LCNT = scl_cnt->fs_scl_lcnt;
iic_reg_ptr->IC_HS_SCL_HCNT = scl_cnt->hs_scl_hcnt;
iic_reg_ptr->IC_HS_SCL_LCNT = scl_cnt->hs_scl_lcnt;
dw_iic_enable(iic_reg_ptr);
}
/** Set spike suppression configuration */
Inline void dw_iic_set_spike_len(DW_IIC_REG *iic_reg_ptr, DW_IIC_SPKLEN *spklen)
{
dw_iic_disable(iic_reg_ptr);
iic_reg_ptr->IC_FS_SPKLEN = spklen->fs_spklen;
iic_reg_ptr->IC_HS_SPKLEN = spklen->hs_spklen;
dw_iic_enable(iic_reg_ptr);
}
Inline void dw_iic_flush_tx(DW_IIC_REG *iic_reg_ptr)
{
(void)iic_reg_ptr->IC_CLR_INTR;
}
Inline void dw_iic_flush_rx(DW_IIC_REG *iic_reg_ptr)
{
}
static uint32_t dw_iic_get_slv_state(DW_IIC_REG *iic_reg_ptr)
{
uint32_t status;
uint32_t slv_state = IIC_SLAVE_STATE_FREE;
status = iic_reg_ptr->IC_RAW_INTR_STAT;
if (status & IC_INTR_STAT_GEN_CALL) {
/* General Call address is received and it is acknowledged */
slv_state |= IIC_SLAVE_STATE_GC_REQ;
}
if (status & IC_INTR_STAT_RX_FULL) {
/* master is attempting to write data to this slave */
slv_state |= IIC_SLAVE_STATE_WR_REQ;
}
if (status & IC_INTR_STAT_RD_REQ) {
/* master is attempting to read data from this slave */
slv_state |= IIC_SLAVE_STATE_RD_REQ;
}
if (status & IC_INTR_STAT_RX_DONE) {
/* master does not acknowledge a transmitted byte, and transmission is done */
slv_state |= IIC_SLAVE_STATE_RD_DONE;
status = iic_reg_ptr->IC_CLR_RX_DONE;
}
if (status & IC_INTR_STAT_START_DET) {
/* a START or RESTART condition has occurred */
slv_state |= IIC_SLAVE_STATE_START;
status = iic_reg_ptr->IC_CLR_START_DET; /* Clear it when read */
}
if (status & IC_INTR_STAT_STOP_DET) {
/* a STOP condition has occurred */
slv_state |= IIC_SLAVE_STATE_STOP;
status = iic_reg_ptr->IC_CLR_STOP_DET; /* Clear it when read */
}
if (status & (IC_INTR_STAT_TX_ABRT|IC_INTR_STAT_TX_OVER\
|IC_INTR_STAT_RX_OVER|IC_INTR_STAT_RX_UNDER)) {
/* error case */
slv_state |= IIC_SLAVE_STATE_ERROR;
status = iic_reg_ptr->IC_CLR_TX_ABRT; /* Clear it when read */
status = iic_reg_ptr->IC_CLR_TX_OVER;
status = iic_reg_ptr->IC_CLR_RX_OVER;
status = iic_reg_ptr->IC_CLR_RX_UNDER;
}
return slv_state;
}
/** Init Designware IIC Device into Master mode */
static void dw_iic_master_init(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t speed_mode, uint32_t addr_mode, uint32_t tar_addr)
{
uint32_t ic_con_val = 0;
DW_IIC_REG *iic_reg_ptr = iic_ctrl_ptr->dw_iic_regs;
dw_iic_disable(iic_reg_ptr);
/* disable all iic interrupt */
iic_reg_ptr->IC_INTR_MASK = IC_INT_DISABLE_ALL;
/* Set to 7bit addressing and update target address */
iic_reg_ptr->IC_TAR = (tar_addr & IC_TAR_10BIT_ADDR_MASK) | IC_TAR_SPECIAL | IC_TAR_GC_OR_START;
/* master mode, restart enabled */
ic_con_val = dw_iic_select_speedmode(speed_mode) | IC_CON_ENA_MASTER_MODE | IC_CON_RESTART_EN;
#if DW_IIC_DYNAMIC_TAR_UPDATE_SUPPORT
if (addr_mode == IIC_10BIT_ADDRESS) {
iic_reg_ptr->IC_TAR |= MST_10_BIT_ADDR_MODE;
}
#else
if (addr_mode == IIC_10BIT_ADDRESS) {
ic_con_val |= MST_10_BIT_ADDR_MODE;
}
#endif
/* Set final IC_CON value */
iic_reg_ptr->IC_CON = ic_con_val;
/* FIFO threshold settings */
iic_reg_ptr->IC_TX_TL = IIC_TX_THRESHOLD;
iic_reg_ptr->IC_RX_TL = IIC_RX_THRESHOLD;
/* Master code settings */
iic_reg_ptr->IC_HS_MADDR = iic_ctrl_ptr->iic_master_code;
dw_iic_enable(iic_reg_ptr);
/* Clock Settings */
dw_iic_set_scl_cnt(iic_reg_ptr, &(iic_ctrl_ptr->iic_scl_cnt));
dw_iic_set_spike_len(iic_reg_ptr, &(iic_ctrl_ptr->iic_spklen));
}
/** Init Designware IIC Device into Slave mode */
static void dw_iic_slave_init(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t addr_mode, uint32_t slv_addr)
{
uint32_t ic_con_val = 0;
DW_IIC_REG *iic_reg_ptr = iic_ctrl_ptr->dw_iic_regs;
dw_iic_disable(iic_reg_ptr);
/* disable all iic interrupt */
iic_reg_ptr->IC_INTR_MASK = IC_INT_DISABLE_ALL;
/* Set slave device address as a slave */
iic_reg_ptr->IC_SAR = slv_addr & IC_SAR_10BIT_ADDR_MASK;
/* slave mode, 7 bit slave address */
ic_con_val = IC_CON_ENA_SLAVE_MODE;
/* If addr mode select to be 10 bit address mode */
if (addr_mode == IIC_10BIT_ADDRESS) {
ic_con_val |= SLV_10_BIT_ADDR_MODE;
}
/* Set final IC_CON value */
iic_reg_ptr->IC_CON = ic_con_val;
/* FIFO threshold settings */
iic_reg_ptr->IC_TX_TL = IIC_TX_THRESHOLD;
iic_reg_ptr->IC_RX_TL = IIC_RX_THRESHOLD;
dw_iic_enable(iic_reg_ptr);
}
/** Check error for IIC master device */
static int32_t dw_iic_mst_chkerr(DW_IIC_CTRL *iic_ctrl_ptr)
{
uint32_t status;
int32_t ercd = IIC_ERR_NONE;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
status = iic_reg_ptr->IC_RAW_INTR_STAT;
if (status & IC_INTR_STAT_TX_ABRT) {
status = iic_reg_ptr->IC_TX_ABRT_SOURCE;
if (status & IIC_MST_ABRT_LOST_BUS) {
ercd = IIC_ERR_LOST_BUS;
} else if (status & IIC_MST_ABRT_ADDR_NOACK) {
ercd = IIC_ERR_ADDR_NOACK;
} else if (status & IIC_MST_ABRT_DATA_NOACK) {
ercd = IIC_ERR_DATA_NOACK;
} else {
ercd = IIC_ERR_UNDEF;
}
status = iic_reg_ptr->IC_CLR_TX_ABRT;
} else {
if (status & IC_INTR_STAT_TX_OVER) {
iic_ctrl_ptr->iic_tx_over ++;
status = iic_reg_ptr->IC_CLR_TX_OVER;
}
if (status & (IC_INTR_STAT_RX_OVER|IC_INTR_STAT_RX_UNDER)) {
iic_ctrl_ptr->iic_rx_over ++;
status = iic_reg_ptr->IC_CLR_RX_OVER;
status = iic_reg_ptr->IC_CLR_RX_UNDER;
}
}
return ercd;
}
/** Check error for IIC slave device */
static int32_t dw_iic_slv_chkerr(DW_IIC_CTRL *iic_ctrl_ptr)
{
uint32_t status;
int32_t ercd = IIC_ERR_NONE;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
status = iic_reg_ptr->IC_RAW_INTR_STAT;
if (status & IC_INTR_STAT_TX_ABRT) {
status = iic_reg_ptr->IC_TX_ABRT_SOURCE;
if (status & IIC_SLV_ABRT_LOST_BUS) {
ercd = IIC_ERR_LOST_BUS;
} else if (status & IC_TX_ABRT_SLVFLUSH_TXFIFO) {
/* Flush tx fifo */
status = iic_reg_ptr->IC_TX_ABRT_SOURCE;
} else {
ercd = IIC_ERR_UNDEF;
}
status = iic_reg_ptr->IC_CLR_TX_ABRT;
} else {
if (status & IC_INTR_STAT_TX_OVER) {
iic_ctrl_ptr->iic_tx_over ++;
status = iic_reg_ptr->IC_CLR_TX_OVER;
}
if (status & (IC_INTR_STAT_RX_OVER|IC_INTR_STAT_RX_UNDER)) {
iic_ctrl_ptr->iic_rx_over ++;
status = iic_reg_ptr->IC_CLR_RX_OVER;
status = iic_reg_ptr->IC_CLR_RX_UNDER;
}
}
return ercd;
}
/** enable designware iic */
static void dw_iic_enable_device(DEV_IIC_INFO *iic_info_ptr)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
if ((iic_info_ptr->status & DEV_ENABLED) == 0) {
dw_iic_enable(iic_reg_ptr);
iic_info_ptr->status |= DEV_ENABLED;
}
}
/** disable designware iic */
static void dw_iic_disable_device(DEV_IIC_INFO *iic_info_ptr)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
uint32_t i;
for (i=0; i<DW_IIC_DISABLE_MAX_T_POLL_CNT; i++) {
dw_iic_disable(iic_reg_ptr);
if ((iic_reg_ptr->IC_ENABLE_STATUS & IC_ENABLE_STATUS_IC_EN) == 0) {
break;
}
}
iic_info_ptr->status &= ~DEV_ENABLED;
}
static void dw_iic_reset_device(DEV_IIC_INFO *iic_info_ptr)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
dw_iic_disable_device(iic_info_ptr);
dw_iic_clear_interrupt_all(iic_reg_ptr);
iic_info_ptr->next_cond = IIC_MODE_STOP;
iic_info_ptr->cur_state = IIC_FREE;
iic_info_ptr->err_state = IIC_ERR_NONE;
iic_ctrl_ptr->iic_tx_over = 0;
iic_ctrl_ptr->iic_rx_over = 0;
dw_iic_enable_device(iic_info_ptr);
}
/** Disable iic master interrupt for transmit or receive */
static void dw_iic_mst_dis_cbr(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t cbrtn)
{
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
switch (cbrtn) {
case DW_IIC_RDY_SND:
dw_iic_mask_interrupt(iic_reg_ptr, IC_INT_MST_TX_ENABLE);
iic_ctrl_ptr->int_status &= ~DW_IIC_TXINT_ENABLE;
break;
case DW_IIC_RDY_RCV:
dw_iic_mask_interrupt(iic_reg_ptr, IC_INT_MST_RX_ENABLE);
iic_ctrl_ptr->int_status &= ~DW_IIC_RXINT_ENABLE;
break;
default:
break;
}
}
/** Disable iic slave interrupt for transmit or receive */
static void dw_iic_slv_dis_cbr(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t cbrtn)
{
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
switch (cbrtn) {
case DW_IIC_RDY_SND:
dw_iic_mask_interrupt(iic_reg_ptr, IC_INT_SLV_TX_ENABLE);
iic_ctrl_ptr->int_status &= ~DW_IIC_TXINT_ENABLE;
break;
case DW_IIC_RDY_RCV:
dw_iic_mask_interrupt(iic_reg_ptr, IC_INT_SLV_RX_ENABLE);
iic_ctrl_ptr->int_status &= ~DW_IIC_RXINT_ENABLE;
break;
default:
break;
}
}
/** Enable iic master interrupt for transmit or receive */
static void dw_iic_mst_ena_cbr(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t cbrtn)
{
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
switch (cbrtn) {
case DW_IIC_RDY_SND:
iic_ctrl_ptr->int_status |= DW_IIC_TXINT_ENABLE;
dw_iic_unmask_interrupt(iic_reg_ptr, IC_INT_MST_TX_ENABLE);
break;
case DW_IIC_RDY_RCV:
iic_ctrl_ptr->int_status |= DW_IIC_RXINT_ENABLE;
dw_iic_unmask_interrupt(iic_reg_ptr, IC_INT_MST_RX_ENABLE);
break;
default:
break;
}
}
/** Enable iic slave interrupt for transmit or receive */
static void dw_iic_slv_ena_cbr(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t cbrtn)
{
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
switch (cbrtn) {
case DW_IIC_RDY_SND:
iic_ctrl_ptr->int_status |= DW_IIC_TXINT_ENABLE;
dw_iic_unmask_interrupt(iic_reg_ptr, IC_INT_SLV_TX_ENABLE);
break;
case DW_IIC_RDY_RCV:
iic_ctrl_ptr->int_status |= DW_IIC_RXINT_ENABLE;
dw_iic_unmask_interrupt(iic_reg_ptr, IC_INT_SLV_RX_ENABLE);
break;
default:
break;
}
}
/**
* \brief disable designware iic send or receive interrupt
* \param[in] DEV_IIC_INFO *iic_info_ptr
* \param[in] cbrtn control code of callback routine of send or receive
*/
static void dw_iic_dis_cbr(DEV_IIC_INFO *iic_info_ptr, uint32_t cbrtn)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
dw_iic_mst_dis_cbr(iic_ctrl_ptr, cbrtn);
} else {
dw_iic_slv_dis_cbr(iic_ctrl_ptr, cbrtn);
}
if (iic_ctrl_ptr->int_status & DW_IIC_GINT_ENABLE) {
if ((iic_ctrl_ptr->int_status & (DW_IIC_RXINT_ENABLE|DW_IIC_TXINT_ENABLE)) == 0) {
int_disable(iic_ctrl_ptr->intno);
iic_ctrl_ptr->int_status &= ~DW_IIC_GINT_ENABLE;
}
}
}
/**
* \brief enable DesignWare IIC send or receive interrupt
* \param[in] DEV_IIC_INFO *iic_info_ptr
* \param[in] cbrtn control code of callback routine of send or receive
*/
static void dw_iic_ena_cbr(DEV_IIC_INFO *iic_info_ptr, uint32_t cbrtn)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
dw_iic_mst_ena_cbr(iic_ctrl_ptr, cbrtn);
} else {
dw_iic_slv_ena_cbr(iic_ctrl_ptr, cbrtn);
}
if ((iic_ctrl_ptr->int_status & DW_IIC_GINT_ENABLE) == 0) {
if (iic_ctrl_ptr->int_status & (DW_IIC_RXINT_ENABLE|DW_IIC_TXINT_ENABLE)) {
iic_ctrl_ptr->int_status |= DW_IIC_GINT_ENABLE;
int_enable(iic_ctrl_ptr->intno);
}
}
}
/**
* \brief enable designware iic interrupt
* \param iic_info_ptr iic information structure pointer
*/
static void dw_iic_enable_interrupt(DEV_IIC_INFO *iic_info_ptr)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
int_handler_install(iic_ctrl_ptr->intno, iic_ctrl_ptr->dw_iic_int_handler);
iic_ctrl_ptr->int_status |= DW_IIC_GINT_ENABLE;
int_enable(iic_ctrl_ptr->intno); /** enable iic interrupt */
}
/**
* \brief disable designware iic interrupt
* \param iic_info_ptr iic information structure pointer
*/
static void dw_iic_disable_interrupt(DEV_IIC_INFO *iic_info_ptr)
{
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
/** disable iic send&receive interrupt after disable iic interrupt */
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
/* disable iic interrupt */
int_disable(iic_ctrl_ptr->intno);
iic_ctrl_ptr->int_status &= ~(DW_IIC_GINT_ENABLE|DW_IIC_TXINT_ENABLE|DW_IIC_RXINT_ENABLE);
}
/** abort current interrupt transmit transfer */
static void dw_iic_abort_tx(DEV_IIC *iic_obj)
{
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
if (iic_ctrl_ptr->int_status & DW_IIC_TXINT_ENABLE) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
iic_info_ptr->status |= DEV_IN_TX_ABRT;
if (iic_info_ptr->iic_cbs.tx_cb != NULL) {
iic_info_ptr->iic_cbs.tx_cb(iic_obj);
}
iic_info_ptr->status &= ~(DEV_IN_TX_ABRT);
}
}
/** abort current interrupt receive transfer */
static void dw_iic_abort_rx(DEV_IIC *iic_obj)
{
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
if (iic_ctrl_ptr->int_status & DW_IIC_RXINT_ENABLE) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
iic_info_ptr->status |= DEV_IN_RX_ABRT;
if (iic_info_ptr->iic_cbs.rx_cb != NULL) {
iic_info_ptr->iic_cbs.rx_cb(iic_obj);
}
iic_info_ptr->status &= ~(DEV_IN_RX_ABRT);
}
}
/** Get available transmit fifo count */
static int32_t dw_iic_get_txavail(DW_IIC_CTRL *iic_ctrl_ptr)
{
int32_t tx_avail = 0;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
if (iic_ctrl_ptr->tx_fifo_len <= 1) {
if (dw_iic_putready(iic_reg_ptr) == 1) {
tx_avail = 1;
} else {
tx_avail = 0;
}
} else {
tx_avail = iic_ctrl_ptr->tx_fifo_len - iic_reg_ptr->IC_TXFLR;
}
return tx_avail;
}
/** Get available receive fifo count */
static int32_t dw_iic_get_rxavail(DW_IIC_CTRL *iic_ctrl_ptr)
{
int32_t rx_avail = 0;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
if (iic_ctrl_ptr->rx_fifo_len <= 1) {
if (dw_iic_getready(iic_reg_ptr) == 1) {
rx_avail = 1;
} else {
rx_avail = 0;
}
} else {
rx_avail = iic_reg_ptr->IC_RXFLR;
}
return rx_avail;
}
/**
* IIC Master device transmit 1 data,
* next_cond can be \ref IC_DATA_CMD_STOP,
* \ref IC_DATA_CMD_RESTART and ref IC_DATA_CMD_NONE
*/
static int32_t dw_iic_mst_write_data(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t data, uint32_t next_cond)
{
uint32_t i = 0;
int32_t ercd = IIC_ERR_NONE;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
while (dw_iic_putready(iic_reg_ptr) == 0) {
if (i++ > iic_ctrl_ptr->retry_cnt) return IIC_ERR_TIMEOUT;
ercd = dw_iic_mst_chkerr(iic_ctrl_ptr);
if (ercd != IIC_ERR_NONE) return ercd;
}
dw_iic_putdata(iic_reg_ptr, data|IC_DATA_CMD_WRITE_REQ|next_cond);
return ercd;
}
/** IIC Slave device transmit 1 data */
static int32_t dw_iic_slv_write_data(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t data)
{
uint32_t i = 0;
int32_t ercd = IIC_ERR_NONE;
uint32_t slv_state, temp;
uint32_t ready2send = 0;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
for (i = 0; i < iic_ctrl_ptr->retry_cnt; i++) {
ercd = dw_iic_slv_chkerr(iic_ctrl_ptr);
if (ercd != IIC_ERR_NONE) return ercd;
slv_state = iic_reg_ptr->IC_RAW_INTR_STAT;
if (slv_state & IC_INTR_STAT_RD_REQ) {
if (dw_iic_putready(iic_reg_ptr)) {
temp = iic_reg_ptr->IC_CLR_RD_REQ;
ready2send = 1;
break;
}
} else if (slv_state & IC_INTR_STAT_RX_DONE) { /* Put RX Done before STOP */
temp = iic_reg_ptr->IC_CLR_RX_DONE;
return IIC_ERR_MSTSTOP;
} else if (slv_state & IC_INTR_STAT_STOP_DET) {
temp = iic_reg_ptr->IC_CLR_STOP_DET;
return IIC_ERR_MSTSTOP;
}
}
if (ready2send) {
dw_iic_putdata(iic_reg_ptr, data|IC_DATA_CMD_WRITE_REQ);
} else {
ercd = IIC_ERR_TIMEOUT;
}
return ercd;
}
/**
* IIC Master device receive 1 data,
* next_cond can be \ref IC_DATA_CMD_STOP,
* \ref IC_DATA_CMD_RESTART and ref IC_DATA_CMD_NONE
*/
static int32_t dw_iic_mst_read_data(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t *data, uint32_t next_cond)
{
uint32_t i = 0;
int32_t ercd = IIC_ERR_NONE;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
/* Issue a read request */
while (dw_iic_putready(iic_reg_ptr) == 0) {
if (i++ > iic_ctrl_ptr->retry_cnt) return IIC_ERR_TIMEOUT;
ercd = dw_iic_mst_chkerr(iic_ctrl_ptr);
if (ercd != IIC_ERR_NONE) return ercd;
}
dw_iic_putdata(iic_reg_ptr, next_cond|IC_DATA_CMD_READ_REQ);
/* Wait to read data */
i = 0;
while (dw_iic_getready(iic_reg_ptr) == 0) {
if (i++ > iic_ctrl_ptr->retry_cnt) return IIC_ERR_TIMEOUT;
ercd = dw_iic_mst_chkerr(iic_ctrl_ptr);
if (ercd != IIC_ERR_NONE) return ercd;
}
*data = dw_iic_getdata(iic_reg_ptr);
return ercd;
}
/** IIC Slave device receive 1 data */
static int32_t dw_iic_slv_read_data(DW_IIC_CTRL *iic_ctrl_ptr, uint32_t *data)
{
uint32_t i = 0;
int32_t ercd = IIC_ERR_NONE;
uint32_t slv_state, temp;
uint32_t ready2read = 0;
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
for (i = 0; i < iic_ctrl_ptr->retry_cnt; i++) {
ercd = dw_iic_slv_chkerr(iic_ctrl_ptr);
if (ercd != IIC_ERR_NONE) return ercd;
slv_state = iic_reg_ptr->IC_RAW_INTR_STAT;
if (slv_state & IC_INTR_STAT_START_DET) {
temp = iic_reg_ptr->IC_CLR_START_DET;
}
if (slv_state & IC_INTR_STAT_RX_FULL) {
if (dw_iic_getready(iic_reg_ptr)) {
ready2read = 1;
break;
}
} else if (slv_state & IC_INTR_STAT_STOP_DET) {
temp = iic_reg_ptr->IC_CLR_STOP_DET;
return IIC_ERR_MSTSTOP;
}
}
if (ready2read) {
*data = dw_iic_getdata(iic_reg_ptr);
} else {
ercd = IIC_ERR_TIMEOUT;
}
return ercd;
}
/** IIC Master transmit called in interrupt */
static void dw_iic_mst_int_write(DEV_IIC *iic_obj)
{
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL_PTR)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG_PTR)(iic_ctrl_ptr->dw_iic_regs);
uint32_t iic_int_status; /** iic interrupt status */
uint32_t last_cond, xmit_data, xmit_end = 0;
DEV_BUFFER *buf_ptr;
uint8_t *p_charbuf;
if (iic_info_ptr->next_cond == IIC_MODE_STOP) {
last_cond = IC_DATA_CMD_STOP;
} else {
last_cond = IC_DATA_CMD_RESTART;
}
iic_int_status = (iic_reg_ptr->IC_INTR_STAT);
buf_ptr = &(iic_info_ptr->tx_buf);
p_charbuf = (uint8_t *)buf_ptr->buf;
if (p_charbuf) {
if (iic_int_status & IC_INTR_STAT_TX_EMPTY) {
xmit_end = 0;
while (dw_iic_putready(iic_reg_ptr)) {
xmit_data = (uint32_t)(p_charbuf[buf_ptr->ofs])|IC_DATA_CMD_WRITE_REQ;
if (buf_ptr->ofs == (buf_ptr->len-1)) {
xmit_end = 1;
xmit_data |= last_cond;
} else {
xmit_data |= IC_DATA_CMD_NONE;
}
buf_ptr->ofs ++;
dw_iic_putdata(iic_reg_ptr, xmit_data);
if (xmit_end) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
iic_info_ptr->cur_state = IIC_FREE;
if (iic_info_ptr->iic_cbs.tx_cb) {
iic_info_ptr->iic_cbs.tx_cb(iic_obj);
}
/* clear the send buffer pointer */
memset(buf_ptr, 0, sizeof(DEV_BUFFER));
break;
}
}
}
if (iic_int_status & IC_INTR_STAT_TX_OVER) {
iic_ctrl_ptr->iic_tx_over ++;
}
if (iic_int_status & IC_INTR_STAT_TX_ABRT) {
iic_info_ptr->err_state = dw_iic_mst_chkerr(iic_ctrl_ptr);
if (iic_info_ptr->err_state != IIC_ERR_NONE) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
iic_info_ptr->cur_state = IIC_FREE;
if (iic_info_ptr->iic_cbs.err_cb) {
iic_info_ptr->iic_cbs.err_cb(iic_obj);
}
/* clear the send buffer pointer */
memset(buf_ptr, 0, sizeof(DEV_BUFFER));
}
}
} else {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
iic_info_ptr->cur_state = IIC_FREE;
}
/* Clear Interrupt */
iic_int_status = iic_reg_ptr->IC_CLR_INTR;
}
/** IIC Master receive called in interrupt */
static void dw_iic_mst_int_read(DEV_IIC *iic_obj)
{
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL_PTR)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG_PTR)(iic_ctrl_ptr->dw_iic_regs);
uint32_t iic_int_status; /** iic interrupt status */
uint32_t last_cond, xmit_data;
DEV_BUFFER *buf_ptr;
DW_IIC_BUFFER *dw_iic_rxbuf_ptr;
uint8_t *p_charbuf;
if (iic_info_ptr->next_cond == IIC_MODE_STOP) {
last_cond = IC_DATA_CMD_STOP;
} else {
last_cond = IC_DATA_CMD_RESTART;
}
iic_int_status = (iic_reg_ptr->IC_INTR_STAT);
buf_ptr = &(iic_info_ptr->rx_buf);
p_charbuf = (uint8_t *)buf_ptr->buf;
if (p_charbuf) {
dw_iic_rxbuf_ptr = &(iic_ctrl_ptr->dw_iic_rxbuf);
if (iic_int_status & IC_INTR_STAT_TX_EMPTY) {
while (dw_iic_putready(iic_reg_ptr)) {
if (dw_iic_rxbuf_ptr->ofs >= dw_iic_rxbuf_ptr->len) {
dw_iic_mask_interrupt(iic_reg_ptr, IC_INTR_STAT_TX_EMPTY);
break;
}
xmit_data = IC_DATA_CMD_READ_REQ;
if (dw_iic_rxbuf_ptr->ofs == (dw_iic_rxbuf_ptr->len-1)) {
xmit_data |= last_cond;
} else {
xmit_data |= IC_DATA_CMD_NONE;
}
dw_iic_rxbuf_ptr->ofs ++;
dw_iic_putdata(iic_reg_ptr, xmit_data);
}
}
if (iic_int_status & IC_INTR_STAT_RX_FULL) {
while (dw_iic_getready(iic_reg_ptr)) {
p_charbuf[buf_ptr->ofs] = dw_iic_getdata(iic_reg_ptr);
buf_ptr->ofs ++;
if (buf_ptr->ofs >= buf_ptr->len) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
iic_info_ptr->cur_state = IIC_FREE;
if (iic_info_ptr->iic_cbs.rx_cb) {
iic_info_ptr->iic_cbs.rx_cb(iic_obj);
}
/* clear the send buffer pointer */
memset(buf_ptr, 0, sizeof(DEV_BUFFER));
dw_iic_rxbuf_ptr->ofs = 0;
dw_iic_rxbuf_ptr->len = 0;
break;
}
}
}
if (iic_int_status & (IC_INTR_STAT_RX_OVER|IC_INTR_STAT_RX_UNDER)) {
iic_ctrl_ptr->iic_rx_over ++;
}
if (iic_int_status & IC_INTR_STAT_TX_ABRT) {
iic_info_ptr->err_state = dw_iic_mst_chkerr(iic_ctrl_ptr);
if (iic_info_ptr->err_state != IIC_ERR_NONE) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
iic_info_ptr->cur_state = IIC_FREE;
if (iic_info_ptr->iic_cbs.err_cb) {
iic_info_ptr->iic_cbs.err_cb(iic_obj);
}
/* clear the send buffer pointer */
memset(buf_ptr, 0, sizeof(DEV_BUFFER));
dw_iic_rxbuf_ptr->ofs = 0;
dw_iic_rxbuf_ptr->len = 0;
}
}
} else {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
iic_info_ptr->cur_state = IIC_FREE;
}
/* Clear Interrupt */
iic_int_status = iic_reg_ptr->IC_CLR_INTR;
}
/** IIC Slave transmit called in interrupt */
static void dw_iic_slv_int_process(DEV_IIC *iic_obj)
{
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL_PTR)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG_PTR)(iic_ctrl_ptr->dw_iic_regs);
uint32_t iic_int_status; /** iic interrupt status */
iic_int_status = (iic_reg_ptr->IC_INTR_STAT);
if (iic_int_status & IC_INTR_STAT_RD_REQ) { /* Read request from master */
if (iic_info_ptr->iic_cbs.tx_cb) {
iic_info_ptr->iic_cbs.tx_cb(iic_obj);
} else { /* When tx callback function is not set disable this tx int for slave */
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
}
}
if (iic_int_status & IC_INTR_STAT_RX_FULL) { /* Write request from master */
if (iic_info_ptr->iic_cbs.rx_cb) {
iic_info_ptr->iic_cbs.rx_cb(iic_obj);
} else { /* When rx callback function is not set disable this rx int for slave */
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
}
}
if (iic_int_status & IC_INTR_STAT_TX_OVER) {
iic_ctrl_ptr->iic_tx_over ++;
}
if (iic_int_status & (IC_INTR_STAT_RX_OVER|IC_INTR_STAT_RX_UNDER)) {
iic_ctrl_ptr->iic_rx_over ++;
}
if (iic_int_status & IC_INTR_STAT_TX_ABRT) {
iic_info_ptr->err_state = dw_iic_slv_chkerr(iic_ctrl_ptr);
if (iic_info_ptr->err_state != IIC_ERR_NONE) {
if (iic_info_ptr->iic_cbs.err_cb) {
iic_info_ptr->iic_cbs.err_cb(iic_obj);
}
}
}
/* Clear Interrupt */
iic_int_status = iic_reg_ptr->IC_CLR_INTR;
}
/** @} end of group DEVICE_DW_IIC_STATIC */
/**
* \defgroup DEVICE_DW_IIC_IMPLEMENT DesignWare IIC Driver Function API Implement
* \ingroup DEVICE_DW_IIC
* \brief implement device hal iic api with DesignWare IIC
* @{
*/
/**
* \brief open a designware iic device
* \param[in] iic_obj iic device object pointer
* \param[in] mode iic working mode (master or slave)
* \param[in] param When mode is \ref DEV_MASTER_MODE, param stands for \ref dev_iic_info::speed_mode "speed mode",
* when mode is \ref DEV_SLAVE_MODE, param stands for \ref dev_iic_info::slv_addr "slave device 7bit address"
* \retval E_OK Open successfully without any issues
* \retval E_OPNED If device was opened before with different parameters,
* then just increase the \ref dev_iic_info::opn_cnt "opn_cnt" and return \ref E_OPNED
* \retval E_OBJ Device object is not valid
* \retval E_SYS Device is opened for different mode before, if you want to open it with different mode, you need to fully close it first.
* \retval E_PAR Parameter is not valid
* \retval E_NOSPT Open settings are not supported
*/
int32_t dw_iic_open (DEV_IIC *iic_obj, uint32_t mode, uint32_t param)
{
int32_t ercd = E_OK;
uint32_t support_modes;
uint32_t param2check;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
DW_IIC_CHECK_EXP((mode==DEV_MASTER_MODE)||(mode==DEV_SLAVE_MODE), E_PAR);
if (mode == DEV_MASTER_MODE) {
DW_IIC_CHECK_EXP((param>=IIC_SPEED_STANDARD) && (param<=IIC_SPEED_ULTRA), E_PAR);
}
/* END OF ERROR CHECK */
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
/* Check supported modes, master or slave */
support_modes = iic_ctrl_ptr->support_modes;
DW_IIC_CHECK_EXP( (((support_modes)&DW_IIC_MASTER_SUPPORTED)&&(mode == DEV_MASTER_MODE)) || \
(((support_modes)&DW_IIC_SLAVE_SUPPORTED)&&(mode == DEV_SLAVE_MODE)), E_NOSPT);
/** Check opened before use case */
if (iic_info_ptr->opn_cnt > 0) {
if (mode != iic_info_ptr->mode) {
/* current working mode is different from passing mode */
return E_SYS;
}
if (mode == DEV_MASTER_MODE) { /* param is speed_mode when as master */
param2check = iic_info_ptr->speed_mode;
} else { /* param is slv_addr when as slave */
param2check = iic_info_ptr->slv_addr;
}
iic_info_ptr->opn_cnt ++;
if (param != param2check) { /* open with different speed mode */
return E_OPNED;
} else {
return E_OK;
}
}
/* auto increase open count */
iic_info_ptr->opn_cnt ++;
iic_info_ptr->mode = mode;
if (iic_info_ptr->addr_mode == IIC_7BIT_ADDRESS) {
iic_info_ptr->tar_addr &= IIC_7BIT_ADDRESS_MASK;
iic_info_ptr->slv_addr &= IIC_7BIT_ADDRESS_MASK;
} else {
iic_info_ptr->addr_mode = IIC_10BIT_ADDRESS;
iic_info_ptr->tar_addr &= IIC_10BIT_ADDRESS_MASK;
iic_info_ptr->slv_addr &= IIC_10BIT_ADDRESS_MASK;
}
/* Do FIFO Length get before init */
#if DW_IIC_CALC_FIFO_LEN_ENABLE
iic_ctrl_ptr->tx_fifo_len = dw_iic_get_txfifo_len(iic_ctrl_ptr->dw_iic_regs);
iic_ctrl_ptr->rx_fifo_len = dw_iic_get_rxfifo_len(iic_ctrl_ptr->dw_iic_regs);
#endif
/* Disable device before init it */
dw_iic_disable_device(iic_info_ptr);
if (mode == DEV_MASTER_MODE) {
iic_info_ptr->speed_mode = param;
dw_iic_master_init(iic_ctrl_ptr, param, iic_info_ptr->addr_mode, iic_info_ptr->tar_addr);
} else {
iic_info_ptr->slv_addr = param;
dw_iic_slave_init(iic_ctrl_ptr, iic_info_ptr->addr_mode, param);
}
iic_info_ptr->status = DEV_ENABLED;
iic_info_ptr->cur_state = IIC_FREE;
iic_info_ptr->err_state = IIC_ERR_NONE;
iic_info_ptr->next_cond = IIC_MODE_STOP;
iic_info_ptr->extra = NULL;
iic_ctrl_ptr->iic_tx_over = 0;
iic_ctrl_ptr->iic_rx_over = 0;
iic_ctrl_ptr->int_status = 0;
memset(&(iic_ctrl_ptr->dw_iic_rxbuf), 0, sizeof(DW_IIC_BUFFER));
iic_ctrl_ptr->dw_iic_rxbuf.buf = &(iic_info_ptr->rx_buf);
/** install iic interrupt into system */
dw_iic_disable_interrupt(iic_info_ptr);
int_handler_install(iic_ctrl_ptr->intno, iic_ctrl_ptr->dw_iic_int_handler);
memset(&(iic_info_ptr->tx_buf), 0, sizeof(DEV_BUFFER));
memset(&(iic_info_ptr->rx_buf), 0, sizeof(DEV_BUFFER));
memset(&(iic_info_ptr->iic_cbs), 0, sizeof(DEV_IIC_CBS));
error_exit:
return ercd;
}
/**
* \brief Close a DesignWare IIC device
* \param[in] iic_obj iic device object pointer
* \retval E_OK Close successfully without any issues(including secenary that device is already closed)
* \retval E_OPNED Device is still opened, the device \ref dev_iic_info::opn_cnt "opn_cnt" decreased by 1
* \retval E_OBJ Device object is not valid
*/
int32_t dw_iic_close (DEV_IIC *iic_obj)
{
int32_t ercd = E_OK;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
DW_IIC_CHECK_EXP(iic_info_ptr->opn_cnt > 0, E_OK);
/* END OF ERROR CHECK */
iic_info_ptr->opn_cnt --;
if (iic_info_ptr->opn_cnt == 0) {
dw_iic_disable_interrupt(iic_info_ptr);
dw_iic_abort_tx(iic_obj);
dw_iic_abort_rx(iic_obj);
memset(&(iic_info_ptr->tx_buf), 0, sizeof(DEV_BUFFER));
memset(&(iic_info_ptr->rx_buf), 0, sizeof(DEV_BUFFER));
memset(&(iic_info_ptr->iic_cbs), 0, sizeof(DEV_IIC_CBS));
dw_iic_disable_device(iic_info_ptr);
iic_info_ptr->status = DEV_DISABLED;
iic_info_ptr->next_cond = IIC_MODE_STOP;
iic_info_ptr->extra = NULL;
} else {
ercd = E_OPNED;
}
error_exit:
return ercd;
}
/**
* \brief Control iic by ctrl command
* \param[in] iic_obj iic device object pointer
* \param[in] ctrl_cmd \ref DEVICE_HAL_IIC_CTRLCMD "control command", to change or get some thing related to iic
* \param[in,out] param parameters that maybe argument of the command,
* or return values of the command, must not be NULL
* \retval E_OK Control device successfully
* \retval E_CLSED Device is not opened
* \retval E_OBJ Device object is not valid or not exists
* \retval E_PAR Parameter is not valid for current control command
* \retval E_SYS Control device failed, due to hardware issues, such as device is disabled
* \retval E_CTX Control device failed, due to different reasons like in transfer state
* \retval E_NOSPT Control command is not supported or not valid
*/
int32_t dw_iic_control (DEV_IIC *iic_obj, uint32_t ctrl_cmd, void *param)
{
int32_t ercd = E_OK;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
DW_IIC_CHECK_EXP(iic_info_ptr->opn_cnt > 0, E_CLSED);
/* END OF ERROR CHECK */
uint32_t val32; /** to receive unsigned int value */
DEV_BUFFER *devbuf;
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL *)(iic_info_ptr->iic_ctrl);
DW_IIC_REG *iic_reg_ptr = (DW_IIC_REG *)(iic_ctrl_ptr->dw_iic_regs);
/* check whether current device is disabled */
if ((iic_info_ptr->status & DEV_ENABLED) == 0) {
/** When device is disabled,
* only IIC_CMD_ENA_DEV, IIC_CMD_DIS_DEV, IIC_CMD_GET_STATUS, IIC_CMD_RESET
* are available, other commands will return E_SYS
*/
if ((ctrl_cmd != IIC_CMD_ENA_DEV) && \
(ctrl_cmd != IIC_CMD_DIS_DEV) && \
(ctrl_cmd != IIC_CMD_GET_STATUS) && \
(ctrl_cmd != IIC_CMD_RESET) ) {
return E_SYS;
}
}
switch (ctrl_cmd) {
/* Commmon commands for both master and slave mode */
case IIC_CMD_GET_STATUS:
DW_IIC_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR);
*((int32_t *)param) = iic_info_ptr->status;
break;
case IIC_CMD_ENA_DEV:
dw_iic_enable_device(iic_info_ptr);
break;
case IIC_CMD_DIS_DEV:
dw_iic_disable_device(iic_info_ptr);
break;
case IIC_CMD_RESET:
dw_iic_reset_device(iic_info_ptr);
break;
case IIC_CMD_FLUSH_TX:
dw_iic_flush_tx(iic_reg_ptr);
break;
case IIC_CMD_FLUSH_RX:
dw_iic_flush_rx(iic_reg_ptr);
break;
case IIC_CMD_SET_ADDR_MODE:
val32 = (uint32_t)param;
DW_IIC_CHECK_EXP((val32==IIC_7BIT_ADDRESS) || (val32==IIC_10BIT_ADDRESS), E_PAR);
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
dw_iic_set_mstaddr_mode(iic_reg_ptr, val32);
} else {
dw_iic_set_slvaddr_mode(iic_reg_ptr, val32);
}
iic_info_ptr->addr_mode = val32;
break;
case IIC_CMD_GET_RXAVAIL:
DW_IIC_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR);
*((int32_t *)param) = dw_iic_get_rxavail(iic_ctrl_ptr);
break;
case IIC_CMD_GET_TXAVAIL:
DW_IIC_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR);
*((int32_t *)param) = dw_iic_get_txavail(iic_ctrl_ptr);
break;
case IIC_CMD_SET_TXCB:
DW_IIC_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR);
iic_info_ptr->iic_cbs.tx_cb = param;
break;
case IIC_CMD_SET_RXCB:
DW_IIC_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR);
iic_info_ptr->iic_cbs.rx_cb = param;
break;
case IIC_CMD_SET_ERRCB:
DW_IIC_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR);
iic_info_ptr->iic_cbs.err_cb = param;
break;
case IIC_CMD_ABORT_TX:
dw_iic_abort_tx(iic_obj);
if ((iic_info_ptr->mode == DEV_MASTER_MODE) \
&& (iic_info_ptr->cur_state == IIC_IN_TX)) {
iic_info_ptr->cur_state = IIC_FREE;
}
break;
case IIC_CMD_ABORT_RX:
dw_iic_abort_rx(iic_obj);
if ((iic_info_ptr->mode == DEV_MASTER_MODE) \
&& (iic_info_ptr->cur_state == IIC_IN_RX)) {
iic_info_ptr->cur_state = IIC_FREE;
}
break;
case IIC_CMD_SET_TXINT:
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
DW_IIC_CHECK_EXP(iic_info_ptr->cur_state != IIC_IN_RX, E_CTX);
}
val32 = (uint32_t)param;
if (val32 == 0) {
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_SND);
iic_info_ptr->cur_state = IIC_FREE;
} else {
iic_info_ptr->cur_state = IIC_IN_TX;
dw_iic_ena_cbr(iic_info_ptr, DW_IIC_RDY_SND);
}
break;
case IIC_CMD_SET_RXINT:
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
DW_IIC_CHECK_EXP(iic_info_ptr->cur_state != IIC_IN_TX, E_CTX);
}
val32 = (uint32_t)param;
if (val32 == 0) {
iic_info_ptr->cur_state = IIC_FREE;
dw_iic_dis_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
} else {
iic_info_ptr->cur_state = IIC_IN_RX;
dw_iic_ena_cbr(iic_info_ptr, DW_IIC_RDY_RCV);
}
break;
case IIC_CMD_SET_TXINT_BUF:
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
DW_IIC_CHECK_EXP(iic_info_ptr->cur_state != IIC_IN_TX, E_CTX);
}
DW_IIC_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR);
if (param != NULL) {
devbuf = (DEV_BUFFER *)param;
iic_info_ptr->tx_buf = *devbuf;
iic_info_ptr->tx_buf.ofs = 0;
} else {
iic_info_ptr->tx_buf.buf = NULL;
iic_info_ptr->tx_buf.len = 0;
iic_info_ptr->tx_buf.ofs = 0;
}
break;
case IIC_CMD_SET_RXINT_BUF:
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
DW_IIC_CHECK_EXP(iic_info_ptr->cur_state != IIC_IN_RX, E_CTX);
}
DW_IIC_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR);
if (param != NULL) {
devbuf = (DEV_BUFFER *)param;
iic_info_ptr->rx_buf = *devbuf;
iic_info_ptr->rx_buf.ofs = 0;
iic_ctrl_ptr->dw_iic_rxbuf.ofs = 0;
iic_ctrl_ptr->dw_iic_rxbuf.len = devbuf->len;
} else {
iic_info_ptr->rx_buf.buf = NULL;
iic_info_ptr->rx_buf.len = 0;
iic_info_ptr->rx_buf.ofs = 0;
iic_ctrl_ptr->dw_iic_rxbuf.ofs = 0;
iic_ctrl_ptr->dw_iic_rxbuf.len = 0;
}
break;
/* Master mode only commands */
case IIC_CMD_MST_SET_SPEED_MODE:
DW_IIC_CHECK_EXP(iic_info_ptr->mode == DEV_MASTER_MODE, E_NOSPT);
val32 = (uint32_t)param;
DW_IIC_CHECK_EXP((val32>=IIC_SPEED_STANDARD) && (val32<=IIC_SPEED_ULTRA), E_PAR);
dw_iic_set_speedmode(iic_reg_ptr, val32);
iic_info_ptr->speed_mode = val32;
break;
case IIC_CMD_MST_SET_TAR_ADDR:
DW_IIC_CHECK_EXP(iic_info_ptr->mode == DEV_MASTER_MODE, E_NOSPT);
if (iic_info_ptr->addr_mode == IIC_7BIT_ADDRESS) {
val32 = ((uint32_t)param) & IIC_7BIT_ADDRESS_MASK;
} else {
val32 = ((uint32_t)param) & IIC_10BIT_ADDRESS_MASK;
}
if (val32 != iic_info_ptr->tar_addr) {
dw_iic_set_taraddr(iic_reg_ptr, val32);
iic_info_ptr->tar_addr = val32;
}
break;
case IIC_CMD_MST_SET_NEXT_COND:
DW_IIC_CHECK_EXP(iic_info_ptr->mode == DEV_MASTER_MODE, E_NOSPT);
val32 = (uint32_t)param;
DW_IIC_CHECK_EXP((val32==IIC_MODE_STOP) || (val32==IIC_MODE_RESTART), E_PAR);
iic_info_ptr->next_cond = (uint32_t)param;
break;
/* Slave mode only commands */
case IIC_CMD_SLV_SET_SLV_ADDR:
DW_IIC_CHECK_EXP(iic_info_ptr->mode == DEV_SLAVE_MODE, E_NOSPT);
if (iic_info_ptr->addr_mode == IIC_7BIT_ADDRESS) {
val32 = ((uint32_t)param) & IIC_7BIT_ADDRESS_MASK;
} else {
val32 = ((uint32_t)param) & IIC_10BIT_ADDRESS_MASK;
}
dw_iic_set_slvaddr(iic_reg_ptr, val32);
iic_info_ptr->slv_addr = val32;
break;
case IIC_CMD_SLV_GET_SLV_STATE:
DW_IIC_CHECK_EXP(iic_info_ptr->mode == DEV_SLAVE_MODE, E_NOSPT);
DW_IIC_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR);
*((uint32_t *)param) = dw_iic_get_slv_state(iic_reg_ptr);
break;
default:
ercd = E_NOSPT;
break;
}
error_exit:
return ercd;
}
/**
* \brief poll transmit data through DesignWare IIC as master or slave
* \param[in] iic_obj iic device object pointer
* \param[in] data data that need to send (data must be uint8_t type)
* \param[in] len data length need to send
* \retval >0 Byte count that was successfully sent for poll method,
* it might can't send that much due to \ref \ref dev_iic_info::err_state "different error state".
* \retval E_OBJ Device object is not valid or not exists
* \retval E_PAR Parameter is not valid
* \retval E_CTX Device is still in transfer state
* \retval E_SYS Can't write data to hardware due to hardware issues, such as device is disabled
*/
int32_t dw_iic_write (DEV_IIC *iic_obj, const void *data, uint32_t len)
{
int32_t ercd = E_OK;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
DW_IIC_CHECK_EXP(iic_info_ptr->opn_cnt > 0, E_CLSED);
DW_IIC_CHECK_EXP(iic_info_ptr->status & DEV_ENABLED, E_SYS);
DW_IIC_CHECK_EXP(data!=NULL, E_PAR);
DW_IIC_CHECK_EXP(len>0, E_PAR);
/* END OF ERROR CHECK */
int32_t i = 0;
uint32_t last_cond = 0; /* Last data for transmit, STOP or RESTART */
int32_t error_state = IIC_ERR_NONE;
const uint8_t *p_charbuf = (const uint8_t *)data;
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL_PTR)(iic_info_ptr->iic_ctrl);
if (iic_info_ptr->mode == DEV_MASTER_MODE) { /* Master mode transmit data */
if (iic_info_ptr->next_cond == IIC_MODE_STOP) {
last_cond = IC_DATA_CMD_STOP;
} else {
last_cond = IC_DATA_CMD_RESTART;
}
/* Try to transmit 0 -> (len-1) data */
len = len - 1; /* Last data write differently */
while (i < len) {
error_state = dw_iic_mst_write_data(iic_ctrl_ptr, (uint32_t)(p_charbuf[i]), IC_DATA_CMD_NONE);
if (error_state != IIC_ERR_NONE) {
break;
}
i ++;
}
/* Try to transmit the last data with STOP or RESTART condition */
if (error_state == IIC_ERR_NONE) {
error_state = dw_iic_mst_write_data(iic_ctrl_ptr, (uint32_t)(p_charbuf[len]), last_cond);
if (error_state == IIC_ERR_NONE) {
i = i + 1; /* Add last data into send count */
}
}
} else { /* Slave mode transmit data */
while (i < len) {
error_state = dw_iic_slv_write_data(iic_ctrl_ptr, (uint32_t)(p_charbuf[i]));
if (error_state != IIC_ERR_NONE) {
break;
}
i ++;
}
}
iic_info_ptr->err_state = error_state;
ercd = i;
error_exit:
return ercd;
}
/**
* \brief read data through DesignWare IIC
* \param[in] iic_obj iic device object pointer
* \param[out] data data that need to read (data must be uint8_t type)
* \param[in] len data count need to read
* \retval >0 Byte count that was successfully received for poll method,
* it might can't send that much due to \ref \ref dev_iic_info::err_state "different error state".
* \retval E_OBJ Device object is not valid or not exists
* \retval E_CTX Device is still in transfer state
* \retval E_PAR Parameter is not valid
* \retval E_SYS Can't receive data from hardware due to hardware issues, such as device is disabled
*/
int32_t dw_iic_read (DEV_IIC *iic_obj, void *data, uint32_t len)
{
int32_t ercd = E_OK;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
DW_IIC_CHECK_EXP(iic_info_ptr->opn_cnt > 0, E_CLSED);
DW_IIC_CHECK_EXP(iic_info_ptr->status & DEV_ENABLED, E_SYS);
DW_IIC_CHECK_EXP(data!=NULL, E_PAR);
DW_IIC_CHECK_EXP(len>0, E_PAR);
/* END OF ERROR CHECK */
int32_t i = 0;
uint32_t last_cond = 0; /* Last data for receive, STOP or RESTART */
uint32_t val32 = 0;
int32_t error_state = IIC_ERR_NONE;
uint8_t *p_charbuf = (uint8_t *)data;
DW_IIC_CTRL *iic_ctrl_ptr = (DW_IIC_CTRL_PTR)(iic_info_ptr->iic_ctrl);
if (iic_info_ptr->mode == DEV_MASTER_MODE) { /* Master mode receive data */
if (iic_info_ptr->next_cond == IIC_MODE_STOP) {
last_cond = IC_DATA_CMD_STOP;
} else {
last_cond = IC_DATA_CMD_RESTART;
}
/* Try to receive 0 -> (len-1) data */
len = len - 1; /* Last data write differently */
while (i < len) {
error_state = dw_iic_mst_read_data(iic_ctrl_ptr, &val32, IC_DATA_CMD_NONE);
if (error_state != IIC_ERR_NONE) {
break;
} else {
p_charbuf[i] = (uint8_t)val32;
}
i ++;
}
/* Try to receive the last data with STOP or RESTART condition */
if (error_state == IIC_ERR_NONE) {
error_state = dw_iic_mst_read_data(iic_ctrl_ptr, &val32, last_cond);
if (error_state == IIC_ERR_NONE) {
p_charbuf[len] = (uint8_t)val32;
i = i + 1; /* Add last data into send count */
}
}
} else { /* Slave mode receive data */
while (i < len) {
error_state = dw_iic_slv_read_data(iic_ctrl_ptr, &val32);
if (error_state != IIC_ERR_NONE) {
break;
} else {
p_charbuf[i] = (uint8_t)val32;
}
i ++;
}
}
iic_info_ptr->err_state = error_state;
ercd = i;
error_exit:
return ercd;
}
/**
* \brief DesignWare IIC interrupt processing routine
* \param[in] iic_info_ptr DEV_IIC *iic_obj
* \param[in] ptr extra information
*/
void dw_iic_isr(DEV_IIC *iic_obj, void *ptr)
{
int32_t ercd = E_OK;
DEV_IIC_INFO *iic_info_ptr = &(iic_obj->iic_info);
/* START ERROR CHECK */
VALID_CHK_IIC_INFO_OBJECT(iic_info_ptr);
/* END OF ERROR CHECK */
if (iic_info_ptr->mode == DEV_MASTER_MODE) {
if (iic_info_ptr->cur_state == IIC_IN_TX) {
dw_iic_mst_int_write(iic_obj);
} else {
dw_iic_mst_int_read(iic_obj);
}
} else {
dw_iic_slv_int_process(iic_obj);
}
error_exit:
return;
}
/** @} end of group DEVICE_DW_IIC_IMPLEMENT */