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:
parent
e1f91db622
commit
6d5ec5a0d5
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue