/**************************************************************************** * * Copyright 2020 Samsung Electronics All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 * * 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. * ****************************************************************************/ #ifdef DEVICE_I2C #include <stdbool.h> #include "s1sbp6a.h" #include "s1sbp6a_cmu.h" #include "s1sbp6a_type.h" #include "s1sbp6a_i2c.h" #define BP6A_I2C_DEFAULT_MODE (I2C_AUTO |I2C_INTERRUPT) #define I2C_MAX_FIFO_SIZE 16 bp6a_i2c_priv_t bp6a_i2c_priv[5] = { { .index = 0, .xfer_speed = DEFAULT_I2CXFER_CLOCK, .master = true, .mode = BP6A_I2C_DEFAULT_MODE, .slave_addr = DEFAULT_I2CSLAVE_ADDR, .addrlen = 7, .timeout = DEFAULT_I2C_TIMEOUT, }, { .index = 1, .xfer_speed = DEFAULT_I2CXFER_CLOCK, .master = true, .mode = BP6A_I2C_DEFAULT_MODE, .slave_addr = DEFAULT_I2CSLAVE_ADDR, .addrlen = 7, .timeout = DEFAULT_I2C_TIMEOUT, }, { .index = 2, .xfer_speed = DEFAULT_I2CXFER_CLOCK, .master = true, .mode = BP6A_I2C_DEFAULT_MODE, .slave_addr = DEFAULT_I2CSLAVE_ADDR, .addrlen = 7, .timeout = DEFAULT_I2C_TIMEOUT, }, { .index = 3, .xfer_speed = DEFAULT_I2CXFER_CLOCK, .master = true, .mode = BP6A_I2C_DEFAULT_MODE, .slave_addr = DEFAULT_I2CSLAVE_ADDR, .addrlen = 7, .timeout = DEFAULT_I2C_TIMEOUT, }, { .index = 4, .xfer_speed = DEFAULT_I2CXFER_CLOCK, .master = true, .mode = BP6A_I2C_DEFAULT_MODE, .slave_addr = DEFAULT_I2CSLAVE_ADDR, .addrlen = 7, .timeout = DEFAULT_I2C_TIMEOUT, } }; static uint32_t bp6a_get_i2c_base_addr(uint32_t ch) { return (BP_I2C0_BASE + ch * 0x1000); } static void bp6a_i2c_run_auto_mode(bp6a_i2c_priv_t *priv, bool enable) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->AUTO_CONF), I2C_AUTO_CONF_MASTER_RUN_MASK, I2C_AUTO_CONF_MASTER_RUN(enable)); modifyreg32(&(i2c->INT_EN), I2C_INT_EN_XFER_DONE_MANUAL_EN_MASK, I2C_INT_EN_XFER_DONE_MANUAL_EN(!enable)); } static int bp6a_i2c_xfer_wait_done_auto(bp6a_i2c_priv_t *priv) { int timeout = priv->timeout; while (timeout-- > 0) { if (priv->int_stat & I2C_INT_STAT_XFER_DONE_AUTO_MASK) { return 0; } } return -1; } static int bp6a_i2c_xfer_wait_done_manual(bp6a_i2c_priv_t *priv) { uint32_t timeout = priv->timeout; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); while (timeout-- > 0) { if (getreg32(&(i2c->INT_STAT)) & I2C_INT_STAT_XFER_DONE_MANUAL_MASK) { return 0; } } return -1; } static void bp6a_i2c_set_channel(bp6a_i2c_priv_t *priv, bool tx, bool rx) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->CTL), I2C_CTL_RXCHON_MASK | I2C_CTL_TXCHON_MASK, I2C_CTL_RXCHON(rx) | I2C_CTL_TXCHON(tx)); } static void bp6a_i2c_set_ctl_mode(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->CTL), I2C_CTL_MASTER_MASK, I2C_CTL_MASTER(priv->master)); } static void bp6a_i2c_set_master_addr(bp6a_i2c_priv_t *priv, uint32_t addr) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); /* For auto mode and HS mode only */ modifyreg32(&(i2c->ADDR), I2C_ADDR_MASTERID_MASK, I2C_ADDR_MASTERID(addr)); } static void bp6a_i2c_set_slave_addr(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); if (priv->addrlen == 10) { modifyreg32(&(i2c->CONF), I2C_CONF_ADDR_MODE_MASK, I2C_CONF_ADDR_MODE(1)); } else { modifyreg32(&(i2c->CONF), I2C_CONF_ADDR_MODE_MASK, I2C_CONF_ADDR_MODE(0)); } if (priv->master) { modifyreg32(&(i2c->ADDR), I2C_ADDR_SLAVE_ADDR_MAS_MASK, I2C_ADDR_SLAVE_ADDR_MAS(priv->slave_addr)); modifyreg32(&(i2c->ADDR), I2C_ADDR_SLAVE_ADDR_SLA_MASK, I2C_ADDR_SLAVE_ADDR_SLA(0)); } else modifyreg32(&(i2c->ADDR), I2C_ADDR_SLAVE_ADDR_SLA_MASK, I2C_ADDR_SLAVE_ADDR_SLA(priv->slave_addr)); } static void bp6a_i2c_set_hs_mode_timing(BP_I2C_TypeDef *i2c, uint8_t clkDiv, uint8_t tSTART_SU, uint16_t tSTART_HD, uint8_t tSTOP_SU, uint8_t tDATA_SU, uint8_t tDATA_HD, uint8_t tSCL_L, uint8_t tSCL_H, uint8_t tSR_RELEASE) { putreg32(&(i2c->TIMING_HS1), I2C_TIMING_HS1_TSDA_SU_HS(0) | I2C_TIMING_HS1_TSTOP_SU_HS(tSTOP_SU) | I2C_TIMING_HS1_TSTART_HD_HS(tSTART_HD) | I2C_TIMING_HS1_TSTART_SU_HS(tSTART_SU)); putreg32(&(i2c->TIMING_HS2), I2C_TIMING_HS2_TSCL_H_HS(tSCL_H) | I2C_TIMING_HS2_TSCL_L_HS(tSCL_L) | I2C_TIMING_HS2_TDATA_SU_HS(tDATA_SU)); putreg32(&(i2c->TIMING_HS3), I2C_TIMING_HS3_TSR_RELEASE(tSR_RELEASE) | I2C_TIMING_HS3_CLK_DIV(clkDiv)); putreg32(&(i2c->TIMING_SLA), tDATA_HD & 0xFFFF); } static void bp6a_set_fs_mode_timing(BP_I2C_TypeDef *i2c, uint8_t clkDiv, uint8_t tSTART_SU, uint8_t tSTART_HD, uint8_t tSTOP_SU, uint8_t tDATA_SU, uint16_t tDATA_HD, uint8_t tSCL_L, uint8_t tSCL_H, uint8_t tSR_RELEASE) { putreg32(&(i2c->TIMING_FS1), I2C_TIMING_FS1_TSDA_SU_FS(0) | I2C_TIMING_FS1_TSTOP_SU_FS(tSTOP_SU) | I2C_TIMING_FS1_TSTART_HD_FS(tSTART_HD) | I2C_TIMING_FS1_TSTART_SU_FS(tSTART_SU)); putreg32(&(i2c->TIMING_FS2), I2C_TIMING_FS2_TSCL_H_FS(tSCL_H) | I2C_TIMING_FS2_TSCL_L_FS(tSCL_L) | I2C_TIMING_FS2_TDATA_SU_FS(tDATA_SU)); putreg32(&(i2c->TIMING_FS3), I2C_TIMING_FS3_TSR_RELEASE(tSR_RELEASE) | I2C_TIMING_FS3_CLK_DIV(clkDiv)); putreg32(&(i2c->TIMING_SLA), 0); //tDATA_HD); } static void bp6a_i2c_calculate_timing(bp6a_i2c_priv_t *priv) { uint32_t clkDiv; uint32_t tFTL_CYCLE_SCL; int32_t i = 0; int32_t uTemp0 = 0; int32_t uTemp1 = 0; int32_t uTemp2 = 0; uint32_t ipClk = bp6a_cmu_get_clock_freq(CMU_I2C0_CLK + priv->index); uint32_t opClk = priv->xfer_speed; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->CONF), I2C_CONF_FLT_CYCLE_SDA_MASK | I2C_CONF_FLT_CYCLE_SCL_MASK, I2C_CONF_FLT_CYCLE_SDA(0) | I2C_CONF_FLT_CYCLE_SCL(0)); tFTL_CYCLE_SCL = (getreg32(&(i2c->CONF)) & I2C_CONF_FLT_CYCLE_SCL_MASK) >> I2C_CONF_FLT_CYCLE_SCL_SHIFT; if (tFTL_CYCLE_SCL > 0x2) { uTemp0 = (uint32_t)((float)(ipClk / opClk) - (tFTL_CYCLE_SCL + 3) * 2); } else { uTemp0 = (uint32_t)((float)(ipClk / opClk) - (tFTL_CYCLE_SCL + 1 + 3) * 2); } for (i = 0; i < 256; i++) { uTemp1 = uTemp0 / (i + 1); if (uTemp1 < 256) { uTemp2 = uTemp1 - 2; break; } } clkDiv = i; uint32_t tSCL_H; if (opClk > I2C_FREQ_400KHZ) { tSCL_H = ((uTemp2 + 10) / 3) - 5; } else { tSCL_H = uTemp2 / 2; } uint32_t tSCL_L = uTemp2 - tSCL_H; uint32_t tSTART_SU = tSCL_L; uint32_t tSTART_HD = tSCL_L; uint32_t tSTOP_SU = tSCL_L; uint32_t tDATA_SU = tSCL_L / 2; uint32_t tDATA_HD = tSCL_L / 2; uint32_t tSR_RELEASE = uTemp2; if (opClk > I2C_FREQ_400KHZ) { bp6a_set_fs_mode_timing(i2c, 1, 37, 37, 37, 18, 18, 37, 37, 74); bp6a_i2c_set_hs_mode_timing(i2c, clkDiv, tSTART_SU, tSTART_HD, tSTOP_SU, tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE); } else bp6a_set_fs_mode_timing(i2c, clkDiv, tSTART_SU, tSTART_HD, tSTOP_SU, tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE); } static void bp6a_i2c_set_fifo_level(bp6a_i2c_priv_t *priv, uint32_t tx, uint32_t rx) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->FIFO_CTL), I2C_FIFO_CTL_RXFIFO_TRIG_MASK | I2C_FIFO_CTL_TXFIFO_TRIG_MASK | I2C_FIFO_CTL_RXFIFO_EN_MASK | I2C_FIFO_CTL_TXFIFO_EN_MASK, I2C_FIFO_CTL_RXFIFO_EN(!!rx) | I2C_FIFO_CTL_TXFIFO_EN(!!tx) | I2C_FIFO_CTL_RXFIFO_TRIG(rx) | I2C_FIFO_CTL_TXFIFO_TRIG(tx)); } static void bp6a_i2c_set_timeout(bp6a_i2c_priv_t *priv) { uint32_t en = 1; uint32_t timeoutCount = priv->timeout; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); if (priv->timeout == 0) { timeoutCount = 0xFF; en = 0; } modifyreg32(&(i2c->TIMEOUT), I2C_TIMEOUT_TIMEOUT_EN_MASK | I2C_TIMEOUT_TOUT_COUNT_MASK, I2C_TIMEOUT_TIMEOUT_EN(en) | I2C_TIMEOUT_TOUT_COUNT(timeoutCount | 0xFF00)); } static void bp6a_i2c_reset_txFIFO(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->FIFO_CTL), I2C_FIFO_CTL_TXFIFO_RST_MASK, I2C_FIFO_CTL_TXFIFO_RST(1)); modifyreg32(&(i2c->FIFO_CTL), I2C_FIFO_CTL_TXFIFO_RST_MASK, I2C_FIFO_CTL_TXFIFO_RST(0)); } static void bp6a_i2c_reset_rxFIFO(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->FIFO_CTL), I2C_FIFO_CTL_RXFIFO_RST_MASK, I2C_FIFO_CTL_RXFIFO_RST(1)); modifyreg32(&(i2c->FIFO_CTL), I2C_FIFO_CTL_RXFIFO_RST_MASK, I2C_FIFO_CTL_RXFIFO_RST(0)); } static int bp6a_i2c_out_byte(bp6a_i2c_priv_t *priv, uint8_t data) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); /* Set Data to TX buffer and Send 1 byte */ modifyreg32(&(i2c->MANUAL_CMD), I2C_MANUAL_CMD_TX_DATA_MASK, I2C_MANUAL_CMD_TX_DATA(data)); modifyreg32(&(i2c->MANUAL_CMD), I2C_MANUAL_CMD_SEND_DATA_MASK, I2C_MANUAL_CMD_SEND_DATA(1)); return bp6a_i2c_xfer_wait_done_manual(priv); } static int bp6a_i2c_in_byte(bp6a_i2c_priv_t *priv, bool is_ack) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); /* Looks awkward, but if I2C_RX_ACK is set, ACK is NOT generated */ if (!is_ack) modifyreg32((&i2c->MANUAL_CMD), I2C_MANUAL_CMD_RX_ACK_MASK | I2C_MANUAL_CMD_READ_DATA_MASK, I2C_MANUAL_CMD_RX_ACK(1) | I2C_MANUAL_CMD_READ_DATA(1)); else modifyreg32((&i2c->MANUAL_CMD), I2C_MANUAL_CMD_READ_DATA_MASK, I2C_MANUAL_CMD_READ_DATA(1)); if (bp6a_i2c_xfer_wait_done_manual(priv) < 0) { return -1; } return ((getreg32(&i2c->MANUAL_CMD) & I2C_MANUAL_CMD_RX_DATA_MASK) >> I2C_MANUAL_CMD_RX_DATA_SHIFT); } static void bp6a_i2c_set_buffer(bp6a_i2c_priv_t *priv, struct i2c_msg_s *msgv) { priv->mptr = msgv->buffer; priv->mcnt = msgv->length; priv->cur_msg = 0; } static void bp6a_i2c_set_auto_config(bp6a_i2c_priv_t *priv, bool stop, bool is_read, uint32_t len) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); /* Set Auto Stop */ modifyreg32(&(i2c->AUTO_CONF), I2C_AUTO_CONF_STOP_AFTER_TRANS_MASK, I2C_AUTO_CONF_STOP_AFTER_TRANS(stop)); /* Set Type of transaction : 0(Tx), 1(Rx) */ modifyreg32(&(i2c->AUTO_CONF), I2C_AUTO_CONF_READ_WRITE_MASK, I2C_AUTO_CONF_READ_WRITE(is_read)); /* Set Length of transaction */ modifyreg32(&(i2c->AUTO_CONF), I2C_AUTO_CONF_TRANS_LEN_MASK, I2C_AUTO_CONF_TRANS_LEN(len)); } static uint8_t bp6a_i2c_get_tx_fifo_level(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); return (uint8_t)((getreg32(&i2c->FIFO_STAT) & I2C_FIFO_STAT_TX_FIFO_LEVEL_MASK) >> I2C_FIFO_STAT_TX_FIFO_LEVEL_SHIFT); } static uint8_t bp6a_i2c_get_rx_fifo_level(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); return (uint8_t)((getreg32(&i2c->FIFO_STAT) & I2C_FIFO_STAT_RX_FIFO_LEVEL_MASK) >> I2C_FIFO_STAT_RX_FIFO_LEVEL_SHIFT); } static void bp6a_i2c_tx_handle(bp6a_i2c_priv_t *priv) { int xferCount; int i; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); xferCount = I2C_MAX_FIFO_SIZE - bp6a_i2c_get_tx_fifo_level(priv); if (priv->mcnt < xferCount) { xferCount = priv->mcnt; } for (i = 0; i < xferCount; i++) { putreg32(&(i2c->TXDATA), priv->mptr[priv->cur_msg++]); priv->mcnt--; } if (priv->mcnt == 0) { modifyreg32(&(i2c->INT_EN), I2C_INT_EN_TX_ALMOST_EMPTY_EN_MASK, I2C_INT_EN_TX_ALMOST_EMPTY_EN(0)); } } static void bp6a_i2c_rx_handle(bp6a_i2c_priv_t *priv) { uint32_t fifoCount; uint32_t i; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); fifoCount = bp6a_i2c_get_rx_fifo_level(priv); for (i = 0; i < fifoCount; i++) { priv->mptr[priv->cur_msg++] = getreg32(&(i2c->RXDATA)); } if (priv->mcnt <= priv->cur_msg) modifyreg32(&(i2c->INT_EN), I2C_INT_EN_RX_ALMOST_FULL_EN_MASK, I2C_INT_EN_RX_ALMOST_FULL_EN(0)); } static void i2c_handler(uint32_t ch) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[ch]; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); uint32_t status = getreg32(&(i2c->INT_STAT)); priv->int_stat |= status; if (status & I2C_INT_STAT_TX_ALMOST_EMPTY_MASK) { priv->st_slave_rx_master_tx = 1; bp6a_i2c_tx_handle(priv); putreg32(&(i2c->INT_STAT), I2C_INT_STAT_TX_ALMOST_EMPTY_MASK); } if (status & I2C_INT_STAT_RX_ALMOST_FULL_MASK) { priv->st_slave_tx_master_rx = 1; bp6a_i2c_rx_handle(priv); putreg32(&(i2c->INT_STAT), I2C_INT_STAT_RX_ALMOST_FULL_MASK); } if (status & I2C_INT_STAT_TRAILING_MASK) { bp6a_i2c_rx_handle(priv); putreg32(&(i2c->INT_STAT), I2C_INT_STAT_TRAILING_MASK); } if (status & I2C_INT_STAT_XFER_DONE_AUTO_MASK) { bp6a_i2c_rx_handle(priv); putreg32(&(i2c->INT_STAT), I2C_INT_STAT_XFER_DONE_AUTO_MASK); } if (status & I2C_INT_STAT_TX_OVERRUN_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_TX_OVERRUN_MASK); } if (status & I2C_INT_STAT_RX_OVERRUN_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_RX_OVERRUN_MASK); } if (status & I2C_INT_STAT_RX_UNDERRUN_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_RX_UNDERRUN_MASK); } if (status & I2C_INT_STAT_XFER_ABORT_AUTO_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_XFER_ABORT_AUTO_MASK); } if (status & I2C_INT_STAT_NO_DEV_ACK_AUTO_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_NO_DEV_ACK_AUTO_MASK); } if (status & I2C_INT_STAT_NO_DEV_AUTO_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_NO_DEV_AUTO_MASK); } if (status & I2C_INT_STAT_TIMEOUT_AUTO_MASK) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_TIMEOUT_AUTO_MASK); } if (status & I2C_INT_STAT_SLAVE_ADDR_MATCH_SHIFT) { putreg32(&(i2c->INT_STAT), I2C_INT_STAT_SLAVE_ADDR_MATCH_MASK); } } void I2C0_Handler(void) { i2c_handler(0); NVIC_ClearPendingIRQ(I2C0_IRQn); } void I2C1_Handler(void) { i2c_handler(1); NVIC_ClearPendingIRQ(I2C1_IRQn); } void I2C2_Handler(void) { i2c_handler(2); NVIC_ClearPendingIRQ(I2C2_IRQn); } void I2C3_Handler(void) { i2c_handler(3); NVIC_ClearPendingIRQ(I2C3_IRQn); } void I2C4_Handler(void) { i2c_handler(4); NVIC_ClearPendingIRQ(I2C4_IRQn); } static void bp6a_i2c_set_interruptmode(bp6a_i2c_priv_t *priv) { BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); /* disable interrupt */ uint32_t reg_val = 0; putreg32(&(i2c->INT_EN), 0); if (!(priv->mode & I2C_INTERRUPT)) { return; } if (priv->master) { reg_val = I2C_INT_EN_XFER_DONE_AUTO_EN(1) | I2C_INT_EN_XFER_ABORT_AUTO_EN(1) | I2C_INT_EN_NO_DEV_ACK_AUTO_EN(1) | I2C_INT_EN_NO_DEV_AUTO_EN(1) | I2C_INT_EN_TIMEOUT_AUTO_EN(1); if (priv->mode & I2C_M_READ) { reg_val |= I2C_INT_EN_RX_ALMOST_FULL_EN(1) | I2C_INT_EN_RX_UNDERRUN_EN(1) | I2C_INT_EN_RX_OVERRUN_EN(1) | I2C_INT_EN_TRAILING_EN(1); } else { reg_val |= I2C_INT_EN_TX_ALMOST_EMPTY_EN(1) | I2C_INT_EN_TX_UNDERRUN_EN(1) | I2C_INT_EN_TX_OVERRUN_EN(1); } } else { reg_val = I2C_INT_EN_SLAVE_ADDR_MATCH_EN(1) | I2C_INT_EN_TIMEOUT_AUTO_EN(1); if (priv->mode & I2C_M_READ) { reg_val |= I2C_INT_EN_TRAILING_EN(1) | I2C_INT_EN_RX_UNDERRUN_EN(1) | I2C_INT_EN_RX_ALMOST_FULL_EN(1) | I2C_INT_EN_RX_OVERRUN_EN(1); } else { reg_val |= I2C_INT_EN_TX_ALMOST_EMPTY_EN(1) | I2C_INT_EN_TX_UNDERRUN_EN(1) | I2C_INT_EN_TX_OVERRUN_EN(1); } } putreg32(&(i2c->INT_EN), reg_val); } static void bp6a_i2c_enable_isr(bp6a_i2c_priv_t *priv) { NVIC_DisableIRQ((IRQn_Type)(I2C0_IRQn + priv->index)); if (priv->mode & I2C_INTERRUPT) { NVIC_ClearPendingIRQ((IRQn_Type)(I2C0_IRQn + priv->index)); if (priv->index == 0) { NVIC_SetVector((IRQn_Type)(I2C0_IRQn + priv->index), (uint32_t)I2C0_Handler); } else if (priv->index == 1) { NVIC_SetVector((IRQn_Type)(I2C0_IRQn + priv->index), (uint32_t)I2C1_Handler); } else if (priv->index == 2) { NVIC_SetVector((IRQn_Type)(I2C0_IRQn + priv->index), (uint32_t)I2C2_Handler); } else if (priv->index == 3) { NVIC_SetVector((IRQn_Type)(I2C0_IRQn + priv->index), (uint32_t)I2C3_Handler); } else if (priv->index == 4) { NVIC_SetVector((IRQn_Type)(I2C0_IRQn + priv->index), (uint32_t)I2C4_Handler); } NVIC_EnableIRQ((IRQn_Type)(I2C0_IRQn + priv->index)); } } static int bp6a_i2c_xfer_slave(bp6a_i2c_priv_t *priv, struct i2c_msg_s *msgv) { priv->slave_addr = msgv->addr; bp6a_i2c_set_slave_addr(priv); bp6a_i2c_set_auto_config(priv, !!(msgv->flags & I2C_M_NOSTOP), !!(msgv->flags & I2C_M_READ), msgv->length); bp6a_i2c_set_fifo_level(priv, DEFAULT_I2C_TX_TRIGLVL, DEFAULT_I2C_RX_TRIGLVL); bp6a_i2c_set_channel(priv, !(msgv->flags & I2C_M_NOSTOP), !!(msgv->flags & I2C_M_NOSTOP)); bp6a_i2c_set_interruptmode(priv); bp6a_i2c_run_auto_mode(priv, true); return bp6a_i2c_xfer_wait_done_auto(priv); } static int bp6a_i2c_xfer_master_auto(bp6a_i2c_priv_t *priv, struct i2c_msg_s *msgv) { priv->slave_addr = msgv->addr; bp6a_i2c_set_slave_addr(priv); bp6a_i2c_set_auto_config(priv, !!(msgv->flags & I2C_M_NOSTOP), !!(msgv->flags & I2C_M_READ), msgv->length); bp6a_i2c_set_fifo_level(priv, DEFAULT_I2C_TX_TRIGLVL, DEFAULT_I2C_RX_TRIGLVL); bp6a_i2c_set_channel(priv, !(msgv->flags & I2C_M_NOSTOP), !!(msgv->flags & I2C_M_NOSTOP)); bp6a_i2c_set_interruptmode(priv); bp6a_i2c_run_auto_mode(priv, true); return bp6a_i2c_xfer_wait_done_auto(priv); } static int bp6a_i2c_transfer(uint32_t index, struct i2c_msg_s *msgv) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->mode = BP6A_I2C_DEFAULT_MODE; priv->mode |= msgv->flags; bp6a_i2c_set_buffer(priv, msgv); if (priv->master) { if (priv->mode & I2C_AUTO) { return bp6a_i2c_xfer_master_auto(priv, msgv); } else { return -1; } // return bp6a_i2c_xfer_master_manual(priv, msgv); } else { return bp6a_i2c_xfer_slave(priv, msgv); } } /****************************************************************************** * Public function ******************************************************************************/ void bp6a_i2c_master_init(uint32_t index, uint32_t freq, int addr_len) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->master = true; priv->xfer_speed = freq; priv->addrlen = addr_len; bp6a_cmu_enable_clock((cmu_clock_t)(CMU_I2C0_CLK + index), true); bp6a_i2c_reset(index); bp6a_i2c_set_master_addr(priv, I2C_DEFAULT_MASTER_ADDRESS); /* Set master mode */ bp6a_i2c_set_ctl_mode(priv); priv->timeout = 0xFFFF; bp6a_i2c_set_timeout(priv); /* Set speed */ bp6a_i2c_calculate_timing(priv); bp6a_i2c_reset_rxFIFO(priv); bp6a_i2c_reset_txFIFO(priv); bp6a_i2c_enable_isr(priv); } void bp6a_i2c_slave_init(uint32_t index) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); bp6a_cmu_enable_clock((cmu_clock_t)(CMU_I2C0_CLK + index), true); priv->master = false; priv->mode |= I2C_AUTO | I2C_INTERRUPT; priv->st_slave_tx_master_rx = 0; priv->st_slave_rx_master_tx = 0; /* Set slave mode */ bp6a_i2c_set_ctl_mode(priv); /* Set slave address */ bp6a_i2c_set_slave_addr(priv); /* Enable stretch-mode */ modifyreg32(&(i2c->CONF), I2C_CONF_STRCH_EN_MASK, I2C_CONF_STRCH_EN(1)); /* set tx/rx channel */ bp6a_i2c_set_channel(priv, true, true); /* Enable interrupt */ bp6a_i2c_set_interruptmode(priv); } int bp6a_i2c_get_slave_status(uint32_t index) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; if (priv->st_slave_tx_master_rx) { priv->st_slave_tx_master_rx = 0; return 1; // Master read } if (priv->st_slave_rx_master_tx) { priv->st_slave_rx_master_tx = 0; return 3; // Master is writing } return 0; } int bp6a_i2c_start(uint32_t index) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->MANUAL_CMD), I2C_MANUAL_CMD_SEND_START_MASK, I2C_MANUAL_CMD_SEND_START(1)); return bp6a_i2c_xfer_wait_done_manual(priv); } int bp6a_i2c_stop(uint32_t index) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->MANUAL_CMD), I2C_MANUAL_CMD_SEND_STOP_MASK, I2C_MANUAL_CMD_SEND_STOP(1)); return bp6a_i2c_xfer_wait_done_manual(priv); } void bp6a_i2c_reset(uint32_t index) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; BP_I2C_TypeDef *i2c = (BP_I2C_TypeDef *)bp6a_get_i2c_base_addr(priv->index); modifyreg32(&(i2c->CTL), I2C_CTL_SW_RST_MASK, I2C_CTL_SW_RST(1)); _Wait(100); modifyreg32(&(i2c->CTL), I2C_CTL_SW_RST_MASK, I2C_CTL_SW_RST(0)); } int bp6a_i2c_setaddress(uint32_t index, int addr, int nbits) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->slave_addr = addr; if (nbits == 1) { priv->msgv->flags |= I2C_M_TEN; } return 0; } int bp6a_i2c_write_byte(uint32_t index, int data) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; return bp6a_i2c_out_byte(priv, data); } int bp6a_i2c_read_byte(uint32_t index, bool last) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; return bp6a_i2c_in_byte(priv, last); } int bp6a_i2c_read(uint32_t index, uint8_t *buffer, int buflen, int start, int stop) { struct i2c_msg_s msg; bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->int_stat = 0; /* 7- or 10-bit? */ msg.flags = (priv->addrlen == 10) ? I2C_M_TEN : 0; /* Setup for the transfer */ msg.addr = priv->slave_addr; msg.flags |= I2C_M_READ; if (start) { msg.flags |= I2C_M_NOSTART; } if (stop) { msg.flags |= I2C_M_NOSTOP; } msg.buffer = (uint8_t *) buffer; msg.length = buflen; return bp6a_i2c_transfer(index, &msg); } int bp6a_i2c_write(uint32_t index, const uint8_t *buffer, int buflen, int start, int stop) { struct i2c_msg_s msg; bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->int_stat = 0; /* Setup for the transfer */ msg.addr = priv->slave_addr; msg.flags = (priv->addrlen == 10) ? I2C_M_TEN : 0; if (start) { msg.flags |= I2C_M_NOSTART; } if (stop) { msg.flags |= I2C_M_NOSTOP; } msg.buffer = (uint8_t *) buffer; /* Override const */ msg.length = buflen; return bp6a_i2c_transfer(index, &msg); } void bp6a_i2c_set_slave_address(uint32_t index, int addr, bool is_slave) { bp6a_i2c_priv_t *priv = &bp6a_i2c_priv[index]; priv->slave_addr = addr; if (is_slave) { priv->master = false; bp6a_i2c_set_slave_addr(priv); } } #endif // DEVICE_I2C