Files
2026-06-01 20:15:04 +03:00

838 lines
25 KiB
C

/****************************************************************************
*
* 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