drivers: i2c: added slave support for DW

added slave mode support for I2C designware chip

Signed-off-by: Andrei-Edward Popa <andrei_edward.popa@upb.ro>
This commit is contained in:
Andrei-Edward Popa 2022-02-06 18:32:20 +02:00 committed by Anas Nashif
parent e1f91db622
commit 6d5ec5a0d5
3 changed files with 280 additions and 23 deletions

View file

@ -100,12 +100,15 @@ static inline void i2c_dw_data_ask(const struct device *dev)
data |= IC_DATA_CMD_STOP;
}
clear_bit_intr_mask_tx_empty(reg_base);
write_cmd_data(data, reg_base);
dw->rx_pending++;
dw->request_bytes--;
cnt--;
}
}
static void i2c_dw_data_read(const struct device *dev)
@ -188,6 +191,12 @@ static inline void i2c_dw_transfer_complete(const struct device *dev)
k_sem_give(&dw->device_sync_sem);
}
#ifdef CONFIG_I2C_SLAVE
static inline uint8_t i2c_dw_read_byte_non_blocking(const struct device *dev);
static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint8_t data);
static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev);
#endif
static void i2c_dw_isr(void *arg)
{
const struct device *port = (const struct device *)arg;
@ -236,6 +245,11 @@ static void i2c_dw_isr(void *arg)
* TX FIFO also serves as command queue where read requests
* are written to TX FIFO.
*/
if ((dw->xfr_flags & I2C_MSG_RW_MASK)
== I2C_MSG_READ) {
set_bit_intr_mask_tx_empty(reg_base);
}
if (intr_stat.bits.tx_empty) {
if ((dw->xfr_flags & I2C_MSG_RW_MASK)
== I2C_MSG_WRITE) {
@ -252,13 +266,50 @@ static void i2c_dw_isr(void *arg)
|| (ret != 0)) {
goto done;
}
}
}
/* STOP detected: finish processing this message */
if (intr_stat.bits.stop_det) {
value = read_clr_stop_det(reg_base);
goto done;
}
/* STOP detected: finish processing this message */
if (intr_stat.bits.stop_det) {
value = read_clr_stop_det(reg_base);
goto done;
}
} else {
#ifdef CONFIG_I2C_SLAVE
const struct i2c_slave_callbacks *slave_cb = dw->slave_cfg->callbacks;
uint32_t slave_activity = test_bit_status_activity(reg_base);
uint8_t data;
i2c_dw_slave_read_clear_intr_bits(port);
if (intr_stat.bits.rx_full) {
if (dw->state != I2C_DW_CMD_SEND) {
dw->state = I2C_DW_CMD_SEND;
if (slave_cb->write_requested) {
slave_cb->write_requested(dw->slave_cfg);
}
}
data = i2c_dw_read_byte_non_blocking(port);
if (slave_cb->write_received) {
slave_cb->write_received(dw->slave_cfg, data);
}
}
if (intr_stat.bits.rd_req) {
if (slave_activity) {
read_clr_rd_req(reg_base);
dw->state = I2C_DW_CMD_RECV;
if (slave_cb->read_requested) {
slave_cb->read_requested(dw->slave_cfg, &data);
i2c_dw_write_byte_non_blocking(port, data);
}
if (slave_cb->read_processed) {
slave_cb->read_processed(dw->slave_cfg, &data);
}
}
}
#endif
}
return;
@ -609,9 +660,178 @@ static int i2c_dw_runtime_configure(const struct device *dev, uint32_t config)
return rc;
}
#ifdef CONFIG_I2C_SLAVE
static inline uint8_t i2c_dw_read_byte_non_blocking(const struct device *dev)
{
uint32_t reg_base = get_regs(dev);
if (!test_bit_status_rfne(reg_base)) { /* Rx FIFO must not be empty */
return -EIO;
}
return (uint8_t)read_cmd_data(reg_base);
}
static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint8_t data)
{
uint32_t reg_base = get_regs(dev);
if (!test_bit_status_tfnt(reg_base)) { /* Tx FIFO must not be full */
return;
}
write_cmd_data(data, reg_base);
}
static int i2c_dw_set_master_mode(const struct device *dev)
{
union ic_comp_param_1_register ic_comp_param_1;
uint32_t reg_base = get_regs(dev);
union ic_con_register ic_con;
clear_bit_enable_en(reg_base);
ic_con.bits.master_mode = 1U;
ic_con.bits.slave_disable = 1U;
ic_con.bits.rx_fifo_full = 0U;
write_con(ic_con.raw, reg_base);
set_bit_enable_en(reg_base);
ic_comp_param_1.raw = read_comp_param_1(reg_base);
write_tx_tl(ic_comp_param_1.bits.tx_buffer_depth + 1, reg_base);
write_rx_tl(ic_comp_param_1.bits.rx_buffer_depth + 1, reg_base);
return 0;
}
static int i2c_dw_set_slave_mode(const struct device *dev, uint8_t addr)
{
uint32_t reg_base = get_regs(dev);
union ic_con_register ic_con;
ic_con.raw = read_con(reg_base);
clear_bit_enable_en(reg_base);
ic_con.bits.master_mode = 0U;
ic_con.bits.slave_disable = 0U;
ic_con.bits.rx_fifo_full = 1U;
ic_con.bits.restart_en = 1U;
ic_con.bits.stop_det = 1U;
write_con(ic_con.raw, reg_base);
write_sar(addr, reg_base);
write_intr_mask(~DW_INTR_MASK_RESET, reg_base);
set_bit_enable_en(reg_base);
write_tx_tl(0, reg_base);
write_rx_tl(0, reg_base);
LOG_DBG("I2C: Host registed as Slave Device");
return 0;
}
static int i2c_dw_slave_register(const struct device *dev,
struct i2c_slave_config *cfg)
{
struct i2c_dw_dev_config * const dw = dev->data;
uint32_t reg_base = get_regs(dev);
int ret;
dw->slave_cfg = cfg;
ret = i2c_dw_set_slave_mode(dev, cfg->address);
write_intr_mask(DW_INTR_MASK_RX_FULL |
DW_INTR_MASK_RD_REQ |
DW_INTR_MASK_TX_ABRT |
DW_INTR_MASK_STOP_DET |
DW_INTR_MASK_START_DET, reg_base);
return ret;
}
static int i2c_dw_slave_unregister(const struct device *dev,
struct i2c_slave_config *cfg)
{
struct i2c_dw_dev_config * const dw = dev->data;
int ret;
dw->state = I2C_DW_STATE_READY;
ret = i2c_dw_set_master_mode(dev);
return ret;
}
static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev)
{
struct i2c_dw_dev_config * const dw = dev->data;
union ic_interrupt_register intr_stat;
uint32_t reg_base = get_regs(dev);
const struct i2c_slave_callbacks *slave_cb = dw->slave_cfg->callbacks;
intr_stat.raw = read_intr_stat(reg_base);
if (intr_stat.bits.tx_abrt) {
read_clr_tx_abrt(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.rx_under) {
read_clr_rx_under(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.rx_over) {
read_clr_rx_over(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.tx_over) {
read_clr_tx_over(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.rx_done) {
read_clr_rx_done(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.activity) {
read_clr_activity(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.stop_det) {
read_clr_stop_det(reg_base);
dw->state = I2C_DW_STATE_READY;
if (slave_cb->stop) {
slave_cb->stop(dw->slave_cfg);
}
}
if (intr_stat.bits.start_det) {
read_clr_start_det(reg_base);
dw->state = I2C_DW_STATE_READY;
}
if (intr_stat.bits.gen_call) {
read_clr_gen_call(reg_base);
dw->state = I2C_DW_STATE_READY;
}
}
#endif /* CONFIG_I2C_SLAVE */
static const struct i2c_driver_api funcs = {
.configure = i2c_dw_runtime_configure,
.transfer = i2c_dw_transfer,
#ifdef CONFIG_I2C_SLAVE
.slave_register = i2c_dw_slave_register,
.slave_unregister = i2c_dw_slave_unregister,
#endif /* CONFIG_I2C_SLAVE */
};
static int i2c_dw_initialize(const struct device *dev)

View file

@ -118,6 +118,8 @@ struct i2c_dw_dev_config {
uint8_t request_bytes;
uint8_t xfr_flags;
bool support_hs_mode;
struct i2c_slave_config *slave_cfg;
};
#define Z_REG_READ(__sz) sys_read##__sz

View file

@ -30,25 +30,41 @@ union ic_con_register {
/* IC_DATA_CMD bits */
#define IC_DATA_CMD_DAT_MASK 0xFF
#define IC_DATA_CMD_CMD (1 << 8)
#define IC_DATA_CMD_STOP (1 << 9)
#define IC_DATA_CMD_RESTART (1 << 10)
#define IC_DATA_CMD_CMD BIT(8)
#define IC_DATA_CMD_STOP BIT(9)
#define IC_DATA_CMD_RESTART BIT(10)
/* DesignWare Interrupt bits positions */
#define DW_INTR_STAT_RX_UNDER (1 << 0)
#define DW_INTR_STAT_RX_OVER (1 << 1)
#define DW_INTR_STAT_RX_FULL (1 << 2)
#define DW_INTR_STAT_TX_OVER (1 << 3)
#define DW_INTR_STAT_TX_EMPTY (1 << 4)
#define DW_INTR_STAT_RD_REQ (1 << 5)
#define DW_INTR_STAT_TX_ABRT (1 << 6)
#define DW_INTR_STAT_RX_DONE (1 << 7)
#define DW_INTR_STAT_ACTIVITY (1 << 8)
#define DW_INTR_STAT_STOP_DET (1 << 9)
#define DW_INTR_STAT_START_DET (1 << 10)
#define DW_INTR_STAT_GEN_CALL (1 << 11)
#define DW_INTR_STAT_RESTART_DET (1 << 12)
#define DW_INTR_STAT_MST_ON_HOLD (1 << 13)
#define DW_INTR_STAT_RX_UNDER BIT(0)
#define DW_INTR_STAT_RX_OVER BIT(1)
#define DW_INTR_STAT_RX_FULL BIT(2)
#define DW_INTR_STAT_TX_OVER BIT(3)
#define DW_INTR_STAT_TX_EMPTY BIT(4)
#define DW_INTR_STAT_RD_REQ BIT(5)
#define DW_INTR_STAT_TX_ABRT BIT(6)
#define DW_INTR_STAT_RX_DONE BIT(7)
#define DW_INTR_STAT_ACTIVITY BIT(8)
#define DW_INTR_STAT_STOP_DET BIT(9)
#define DW_INTR_STAT_START_DET BIT(10)
#define DW_INTR_STAT_GEN_CALL BIT(11)
#define DW_INTR_STAT_RESTART_DET BIT(12)
#define DW_INTR_STAT_MST_ON_HOLD BIT(13)
#define DW_INTR_MASK_RX_UNDER BIT(0)
#define DW_INTR_MASK_RX_OVER BIT(1)
#define DW_INTR_MASK_RX_FULL BIT(2)
#define DW_INTR_MASK_TX_OVER BIT(3)
#define DW_INTR_MASK_TX_EMPTY BIT(4)
#define DW_INTR_MASK_RD_REQ BIT(5)
#define DW_INTR_MASK_TX_ABRT BIT(6)
#define DW_INTR_MASK_RX_DONE BIT(7)
#define DW_INTR_MASK_ACTIVITY BIT(8)
#define DW_INTR_MASK_STOP_DET BIT(9)
#define DW_INTR_MASK_START_DET BIT(10)
#define DW_INTR_MASK_GEN_CALL BIT(11)
#define DW_INTR_MASK_RESTART_DET BIT(12)
#define DW_INTR_MASK_MST_ON_HOLD BIT(13)
#define DW_INTR_MASK_RESET 0x000008ff
union ic_interrupt_register {
uint32_t raw;
@ -114,7 +130,16 @@ union ic_comp_param_1_register {
#define DW_IC_REG_RX_TL (0x38)
#define DW_IC_REG_TX_TL (0x3C)
#define DW_IC_REG_CLR_INTR (0x40)
#define DW_IC_REG_CLR_RX_UNDER (0x44)
#define DW_IC_REG_CLR_RX_OVER (0x48)
#define DW_IC_REG_CLR_TX_OVER (0x4c)
#define DW_IC_REG_CLR_RD_REQ (0x50)
#define DW_IC_REG_CLR_TX_ABRT (0x54)
#define DW_IC_REG_CLR_RX_DONE (0x58)
#define DW_IC_REG_CLR_ACTIVITY (0x5c)
#define DW_IC_REG_CLR_STOP_DET (0x60)
#define DW_IC_REG_CLR_START_DET (0x64)
#define DW_IC_REG_CLR_GEN_CALL (0x68)
#define DW_IC_REG_ENABLE (0x6C)
#define DW_IC_REG_STATUS (0x70)
#define DW_IC_REG_TXFLR (0x74)
@ -152,12 +177,22 @@ DEFINE_TEST_BIT_OP(intr_stat_tx_abrt, DW_IC_REG_INTR_STAT, DW_IC_INTR_STAT_TX_AB
DEFINE_MM_REG_WRITE(intr_mask, DW_IC_REG_INTR_MASK, 32)
#define DW_IC_INTR_MASK_TX_EMPTY_BIT (4)
DEFINE_CLEAR_BIT_OP(intr_mask_tx_empty, DW_IC_REG_INTR_MASK, DW_IC_INTR_MASK_TX_EMPTY_BIT)
DEFINE_SET_BIT_OP(intr_mask_tx_empty, DW_IC_REG_INTR_MASK, DW_IC_INTR_MASK_TX_EMPTY_BIT)
DEFINE_MM_REG_WRITE(rx_tl, DW_IC_REG_RX_TL, 32)
DEFINE_MM_REG_WRITE(tx_tl, DW_IC_REG_TX_TL, 32)
DEFINE_MM_REG_READ(clr_intr, DW_IC_REG_CLR_INTR, 32)
DEFINE_MM_REG_READ(clr_stop_det, DW_IC_REG_CLR_STOP_DET, 32)
DEFINE_MM_REG_READ(clr_start_det, DW_IC_REG_CLR_START_DET, 32)
DEFINE_MM_REG_READ(clr_gen_call, DW_IC_REG_CLR_GEN_CALL, 32)
DEFINE_MM_REG_READ(clr_tx_abrt, DW_IC_REG_CLR_TX_ABRT, 32)
DEFINE_MM_REG_READ(clr_rx_under, DW_IC_REG_CLR_RX_UNDER, 32)
DEFINE_MM_REG_READ(clr_rx_over, DW_IC_REG_CLR_RX_OVER, 32)
DEFINE_MM_REG_READ(clr_tx_over, DW_IC_REG_CLR_TX_OVER, 32)
DEFINE_MM_REG_READ(clr_rx_done, DW_IC_REG_CLR_RX_DONE, 32)
DEFINE_MM_REG_READ(clr_rd_req, DW_IC_REG_CLR_RD_REQ, 32)
DEFINE_MM_REG_READ(clr_activity, DW_IC_REG_CLR_ACTIVITY, 32)
#define DW_IC_ENABLE_EN_BIT (0)
DEFINE_CLEAR_BIT_OP(enable_en, DW_IC_REG_ENABLE, DW_IC_ENABLE_EN_BIT)