123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- /*
- * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /******************************************************************************
- * @file dw_iic.c
- * @brief CSI Source File for IIC Driver
- * @version V1.0
- * @date 02. June 2017
- ******************************************************************************/
- #include "csi_core.h"
- #include "drv_iic.h"
- #include "dw_iic.h"
- #include "soc.h"
- #include "string.h"
- #define ERR_IIC(errno) (CSI_DRV_ERRNO_I2C_BASE | errno)
- #define IIC_NULL_PARAM_CHK(para) \
- do { \
- if (para == NULL) { \
- return ERR_IIC(EDRV_PARAMETER); \
- } \
- } while (0)
- typedef struct {
- uint32_t base;
- uint32_t irq;
- iic_event_cb_t cb_event;
- void *cb_arg;
- uint32_t rx_total_num;
- uint32_t tx_total_num;
- void *rx_buf;
- void *tx_buf;
- volatile uint32_t rx_cnt;
- volatile uint32_t tx_cnt;
- uint32_t status; ///< status of iic transfer
- } dw_iic_priv_t;
- static dw_iic_priv_t iic_instance[CONFIG_IIC_NUM];
- static const iic_capabilities_t iic_capabilities = {
- .address_10_bit = 0 /* supports 10-bit addressing */
- };
- static inline void dw_iic_disable(dw_iic_reg_t *addr)
- {
- /* First clear ACTIVITY, then Disable IIC */
- addr->IC_CLR_ACTIVITY;
- addr->IC_ENABLE = DW_IIC_DISABLE;
- }
- static inline void dw_iic_enable(dw_iic_reg_t *addr)
- {
- addr->IC_ENABLE = DW_IIC_ENABLE;
- }
- static inline void dw_iic_set_transfer_speed(dw_iic_reg_t *addr, DWENUM_IIC_SPEED speed)
- {
- uint16_t temp = addr->IC_CON;
- temp &= ~((1 << 1) + (1 << 2));
- temp |= speed << 1;
- addr->IC_CON = temp;
- }
- static inline void dw_iic_set_target_address(dw_iic_reg_t *addr, uint16_t address)
- {
- uint16_t temp = addr->IC_TAR;
- temp &= 0xfc00;
- temp |= address;
- addr->IC_TAR = temp;
- }
- static inline void dw_iic_set_addr_mode(dw_iic_reg_t *addr, iic_address_mode_e addr_mode)
- {
- uint16_t temp = addr->IC_TAR;
- temp &= 0xefff;
- temp |= addr_mode << 12;
- addr->IC_TAR = temp;
- }
- static void dw_i2c_int_clear(dw_iic_reg_t *addr, DWENUM_IIC_INTERRUPT_TYPE type)
- {
- uint32_t temp = 0;
- switch (type) {
- case DW_IIC_RX_UNDER:
- temp = addr->IC_CLR_RX_UNDER;
- break;
- case DW_IIC_RX_OVER:
- temp = addr->IC_CLR_RX_OVER;
- break;
- case DW_IIC_TX_OVER:
- temp = addr->IC_CLR_TX_OVER;
- break;
- case DW_IIC_RD_REQ:
- temp = addr->IC_CLR_RD_REQ;
- break;
- case DW_IIC_TX_ABRT:
- temp = addr->IC_CLR_TX_ABRT;
- break;
- case DW_IIC_RX_DONE:
- temp = addr->IC_CLR_RX_DONE;
- break;
- case DW_IIC_ACTIVITY:
- temp = addr->IC_CLR_ACTIVITY;
- break;
- case DW_IIC_STOP_DET:
- temp = addr->IC_CLR_STOP_DET;
- break;
- case DW_IIC_START_DET:
- temp = addr->IC_CLR_START_DET;
- break;
- case DW_IIC_GEN_CALL:
- temp = addr->IC_CLR_GEN_CALL;
- break;
- default:
- temp = addr->IC_CLR_INTR;
- }
- }
- /**
- \brief interrupt service function for transmit FIFO empty interrupt.
- \param[in] iic_priv pointer to iic private.
- */
- static void dw_iic_intr_tx_empty(dw_iic_priv_t *iic_priv)
- {
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- if (addr->IC_INTR_STAT & (1 << DW_IIC_TX_EMPTY)) {
- uint32_t remain_txfifo = iic_priv->tx_total_num - iic_priv->tx_cnt;
- uint8_t emptyfifo = (remain_txfifo > (DW_IIC_FIFO_MAX_LV - addr->IC_TXFLR)) ? DW_IIC_FIFO_MAX_LV - addr->IC_TXFLR : remain_txfifo;
- uint32_t i = 0u;
- for (i = 0; i < emptyfifo; i++) {
- addr->IC_DATA_CMD = *((uint8_t *)(iic_priv->tx_buf)++);
- }
- iic_priv->tx_cnt += emptyfifo;
- if (iic_priv->tx_cnt == iic_priv->tx_total_num) {
- addr->IC_INTR_MASK &= ~(1 << DW_IIC_TX_EMPTY);
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_TX_OVER)) {
- dw_i2c_int_clear(addr, DW_IIC_TX_OVER);
- iic_priv->status = IIC_STATE_ERROR;
- dw_iic_disable(addr);
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_TX_ABRT)) {
- dw_i2c_int_clear(addr, DW_IIC_TX_ABRT);
- iic_priv->status = IIC_STATE_ERROR;
- dw_iic_disable(addr);
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_STOP_DET)) {
- iic_priv->status = IIC_STATE_DONE;
- dw_i2c_int_clear(addr, DW_IIC_STOP_DET);
- if (iic_priv->cb_event) {
- dw_iic_disable(addr);
- addr->IC_CLR_INTR;
- iic_priv->cb_event(I2C_EVENT_TRANSFER_DONE, iic_priv->cb_arg);
- }
- }
- }
- /**
- \brief interrupt service function for receive FIFO full interrupt .
- \param[in] iic_priv pointer to iic private.
- */
- static void dw_iic_intr_rx_full(dw_iic_priv_t *iic_priv)
- {
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- if (addr->IC_INTR_STAT & (1 << DW_IIC_RX_FULL)) {
- uint8_t emptyfifo = addr->IC_RXFLR;
- uint32_t i = 0u;
- for (i = 0; i < emptyfifo ; i++) {
- *((uint8_t *)(iic_priv->rx_buf++)) = ((addr->IC_DATA_CMD) & 0xff);
- }
- iic_priv->rx_cnt += emptyfifo;
- if (iic_priv->rx_cnt != iic_priv->rx_total_num) {
- addr->IC_DATA_CMD = 1 << 8;
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_RX_OVER)) {
- dw_i2c_int_clear(addr, DW_IIC_RX_OVER);
- iic_priv->status = IIC_STATE_ERROR;
- dw_iic_disable(addr);
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_RX_FULL)) {
- dw_i2c_int_clear(addr, DW_IIC_RX_FULL);
- iic_priv->status = IIC_STATE_ERROR;
- dw_iic_disable(addr);
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- }
- }
- if (addr->IC_INTR_STAT & (1 << DW_IIC_STOP_DET)) {
- dw_i2c_int_clear(addr, DW_IIC_STOP_DET);
- if (iic_priv->rx_cnt == iic_priv->rx_total_num) {
- iic_priv->status = IIC_STATE_DONE;
- dw_iic_disable(addr);
- addr->IC_CLR_INTR;
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_TRANSFER_DONE, iic_priv->cb_arg);
- }
- }
- }
- }
- void dw_iic_irqhandler(int32_t idx)
- {
- dw_iic_priv_t *iic_priv = &iic_instance[idx];
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- if (addr->IC_INTR_STAT & (1 << DW_IIC_TX_ABRT)) {
- /* If arbitration fault, it indicates either a slave device not
- * responding as expected, or other master which is not supported
- * by this SW.
- */
- dw_i2c_int_clear(addr, DW_IIC_TX_ABRT);
- iic_priv->status = IIC_STATE_DONE;
- if (iic_priv->cb_event) {
- dw_iic_disable(addr);
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- return;
- }
- }
- switch (iic_priv->status) {
- /* send data to slave */
- case IIC_STATE_DATASEND: {
- dw_iic_intr_tx_empty(iic_priv);
- break;
- }
- /* wait for data from slave */
- case IIC_STATE_WFDATA: {
- dw_iic_intr_rx_full(iic_priv);
- break;
- }
- /* unexpected state,SW fault */
- default: {
- dw_iic_disable(addr);
- if (iic_priv->cb_event) {
- iic_priv->cb_event(I2C_EVENT_BUS_ERROR, iic_priv->cb_arg);
- }
- }
- }
- }
- int32_t __attribute__((weak)) target_iic_init(pin_t scl, pin_t sda, uint32_t *base, uint32_t *irq)
- {
- return -1;
- }
- /**
- \brief Initialize IIC Interface. 1. Initializes the resources needed for the IIC interface 2.registers event callback function
- \param[in] handle iic handle to operate.
- \param[in] cb_event Pointer to \ref iic_event_cb_t
- \return error code
- */
- iic_handle_t csi_iic_initialize(pin_t scl, pin_t sda, iic_event_cb_t cb_event, void *cb_arg)
- {
- uint32_t base = 0u;
- uint32_t irq = 0u;
- int32_t idx = target_iic_init(scl, sda, &base, &irq);
- if (idx < 0 || idx >= CONFIG_IIC_NUM) {
- return NULL;
- }
- dw_iic_priv_t *iic_priv = &iic_instance[idx];
- iic_priv->base = base;
- iic_priv->irq = irq;
- iic_priv->cb_event = cb_event;
- iic_priv->cb_arg = cb_arg;
- iic_priv->rx_total_num = 0;
- iic_priv->tx_total_num = 0;
- iic_priv->rx_buf = NULL;
- iic_priv->tx_buf = NULL;
- iic_priv->rx_cnt = 0;
- iic_priv->tx_cnt = 0;
- iic_priv->status = 0;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- /* mask all interrupts */
- addr->IC_INTR_MASK = 0x00;
- addr->IC_CON = DW_IIC_CON_DEFAUL;
- addr->IC_INTR_MASK |= 1 << DW_IIC_TX_ABRT;
- addr->IC_INTR_MASK |= 1 << DW_IIC_TX_OVER;
- addr->IC_INTR_MASK |= 1 << DW_IIC_RX_OVER;
- addr->IC_INTR_MASK |= 1 << DW_IIC_RX_FULL;
- addr->IC_INTR_MASK |= 1 << DW_IIC_STOP_DET;
- drv_nvic_enable_irq(iic_priv->irq);
- return iic_priv;
- }
- /**
- \brief De-initialize IIC Interface. stops operation and releases the software resources used by the interface
- \param[in] handle iic handle to operate.
- \return error code
- */
- int32_t csi_iic_uninitialize(iic_handle_t handle)
- {
- IIC_NULL_PARAM_CHK(handle);
- /* First clear ACTIVITY, then Disable IIC */
- dw_iic_priv_t *iic_priv = handle;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- addr->IC_CLR_ACTIVITY;
- addr->IC_INTR_MASK = 0x00;
- addr->IC_ENABLE = DW_IIC_DISABLE;
- iic_priv->cb_event = NULL;
- iic_priv->rx_total_num = 0;
- iic_priv->tx_total_num = 0;
- iic_priv->rx_buf = NULL;
- iic_priv->tx_buf = NULL;
- iic_priv->rx_cnt = 0;
- iic_priv->tx_cnt = 0;
- iic_priv->status = 0;
- drv_nvic_disable_irq(iic_priv->irq);
- return 0;
- }
- /**
- \brief Get driver capabilities.
- \return \ref iic_capabilities_t
- */
- iic_capabilities_t csi_iic_get_capabilities(iic_handle_t handle)
- {
- return iic_capabilities;
- }
- /**
- \brief config iic.
- \param[in] handle iic handle to operate.
- \param[in] mode \ref iic_mode_e.if negative, then this attribute not changed
- \param[in] speed \ref iic_speed_e.if negative, then this attribute not changed
- \param[in] addr_mode \ref iic_address_mode_e.if negative, then this attribute not changed
- \param[in] slave_addr slave address.if negative, then this attribute not changed
- \return error code
- */
- int32_t csi_iic_config(iic_handle_t handle,
- iic_mode_e mode,
- iic_speed_e speed,
- iic_address_mode_e addr_mode,
- int32_t slave_addr)
- {
- IIC_NULL_PARAM_CHK(handle);
- if (mode >= 0) {
- switch (mode) {
- case IIC_MODE_MASTER:
- break;
- case IIC_MODE_SLAVE:
- return ERR_IIC(EDRV_UNSUPPORTED);
- break;
- default:
- return ERR_IIC(EDRV_PARAMETER);
- }
- }
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(((dw_iic_priv_t *)handle)->base);
- if (speed >= 0) {
- switch (speed) {
- case I2C_BUS_SPEED_STANDARD:
- dw_iic_set_transfer_speed(addr, DW_IIC_STANDARDSPEED);
- break;
- case I2C_BUS_SPEED_FAST:
- dw_iic_set_transfer_speed(addr, DW_IIC_FASTSPEED);
- break;
- case I2C_BUS_SPEED_FAST_PLUS:
- return ERR_IIC(EDRV_UNSUPPORTED);
- case I2C_BUS_SPEED_HIGH:
- dw_iic_set_transfer_speed(addr, DW_IIC_HIGHSPEED);
- break;
- default:
- return ERR_IIC(EDRV_PARAMETER);
- }
- }
- if (addr_mode >= 0) {
- switch (addr_mode) {
- case I2C_ADDRESS_10BIT:
- case I2C_ADDRESS_7BIT:
- dw_iic_set_addr_mode(addr, addr_mode);
- break;
- default:
- return ERR_IIC(EDRV_PARAMETER);
- }
- }
- if (slave_addr >= 0) {
- dw_iic_set_target_address(addr, slave_addr);
- }
- return 0;
- }
- /**
- \brief Start transmitting data as I2C Master.
- \param[in] handle iic handle to operate.
- \param[in] data Pointer to buffer with data to transmit to I2C Slave
- \param[in] num Number of data items to send
- \param[in] xfer_pending Transfer operation is pending - Stop condition will not be generated
- \return error code
- */
- int32_t csi_iic_master_send(iic_handle_t handle, const void *data, uint32_t num, bool xfer_pending)
- {
- IIC_NULL_PARAM_CHK(handle);
- if (data == NULL || num == 0) {
- return ERR_IIC(EDRV_PARAMETER);
- }
- dw_iic_priv_t *iic_priv = handle;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- iic_priv->tx_buf = (uint8_t *)data;
- iic_priv->tx_total_num = num;
- iic_priv->tx_cnt = 0;
- iic_priv->status = IIC_STATE_DATASEND;
- dw_iic_disable(addr);
- addr->IC_CLR_INTR;
- uint32_t length = (num > DW_IIC_FIFO_MAX_LV) ? DW_IIC_FIFO_MAX_LV : num;
- addr->IC_TX_TL = DW_IIC_TXFIFO_LV;
- dw_iic_enable(addr);
- uint32_t i = 0u;
- for (i = 0; i < length; i++) {
- addr->IC_DATA_CMD = *((uint8_t *)(iic_priv->tx_buf)++);
- }
- iic_priv->tx_cnt += length;
- /* open corresponding interrupts */
- addr->IC_INTR_MASK |= 1 << DW_IIC_TX_EMPTY;
- return 0;
- }
- /**
- \fn int32_t csi_iic_master_receive (iic_handle_t handle,const void *data, uint32_t num, bool xfer_pending)
- \brief Start receiving data as I2C Master.
- \param[in] handle iic handle to operate.
- \param[out] data Pointer to buffer for data to receive from IIC receiver
- \param[in] num Number of data items to receive
- \param[in] xfer_pending Transfer operation is pending - Stop condition will not be generated
- \return error code
- */
- int32_t csi_iic_master_receive(iic_handle_t handle, const void *data, uint32_t num, bool xfer_pending)
- {
- IIC_NULL_PARAM_CHK(handle);
- if (data == NULL || num == 0) {
- return ERR_IIC(EDRV_PARAMETER);
- }
- dw_iic_priv_t *iic_priv = handle;
- iic_priv->rx_buf = (uint8_t *)data;
- iic_priv->rx_total_num = num;
- iic_priv->rx_cnt = 0;
- iic_priv->status = IIC_STATE_WFDATA;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- dw_iic_disable(addr);
- int32_t tmp = addr->IC_CLR_INTR;
- addr->IC_RX_TL = DW_IIC_FIFO_MAX_LV; /* Sets receive FIFO threshold */
- tmp = addr->IC_CLR_INTR;
- addr->IC_RX_TL = DW_IIC_RXFIFO_LV; /* Sets receive FIFO threshold */
- dw_iic_enable(addr);
- addr->IC_DATA_CMD = 1 << 8;
- return 0;
- }
- /**
- \brief Start transmitting data as I2C Slave.
- \param[in] handle iic handle to operate.
- \param[in] data Pointer to buffer with data to transmit to I2C Master
- \param[in] num Number of data items to send
- \return error code
- */
- int32_t csi_iic_slave_send(iic_handle_t handle, const void *data, uint32_t num)
- {
- return ERR_IIC(EDRV_UNSUPPORTED);
- }
- /**
- \fn int32_t csi_iic_slave_receive (iic_handle_t handle, const void *data, uint32_t num)
- \brief Start receiving data as I2C Slave.
- \param[in] handle iic handle to operate.
- \param[out] data Pointer to buffer for data to receive from I2C Master
- \param[in] num Number of data items to receive
- \return error code
- */
- int32_t csi_iic_slave_receive(iic_handle_t handle, const void *data, uint32_t num)
- {
- return ERR_IIC(EDRV_UNSUPPORTED);
- }
- /**
- \brief abort transfer.
- \param[in] handle iic handle to operate.
- \return error code
- */
- int32_t csi_iic_abort_transfer(iic_handle_t handle)
- {
- IIC_NULL_PARAM_CHK(handle);
- dw_iic_priv_t *iic_priv = handle;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- dw_iic_disable(addr);
- iic_priv->rx_cnt = 0;
- iic_priv->tx_cnt = 0;
- iic_priv->rx_buf = NULL;
- iic_priv->tx_buf = NULL;
- return 0;
- }
- /**
- \brief Get IIC status.
- \param[in] handle iic handle to operate.
- \return IIC status \ref iic_status_t
- */
- iic_status_t csi_iic_get_status(iic_handle_t handle)
- {
- iic_status_t iic_status = {0};
- if (handle == NULL) {
- return iic_status;
- }
- dw_iic_priv_t *iic_priv = handle;
- dw_iic_reg_t *addr = (dw_iic_reg_t *)(iic_priv->base);
- int32_t tmp = addr->IC_STATUS;
- if (tmp & 0x1) {
- iic_status.busy = 1;
- if (tmp & 0x40) {
- iic_status.mode = 0;
- } else {
- iic_status.mode = 1;
- }
- }
- if (iic_priv->status == IIC_STATE_WFDATA) {
- iic_status.direction = 1;
- }
- if (addr->IC_RAW_INTR_STAT & 0x800) {
- iic_status.general_call = 1;
- }
- if (iic_priv->status == IIC_STATE_ERROR) {
- iic_status.bus_error = 1;
- }
- return iic_status;
- }
|