/* * Copyright (c) 2017 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief I2C master driver for Actions SoC */ //#define DT_DRV_COMPAT actions_acts_i2c #include #include #include #include #include #include #include #include #include #include #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL #include LOG_MODULE_REGISTER(i2c_acts_i2c); #define I2C_TIMEOUT_MS Z_TIMEOUT_MS(50) #define I2C_WAIT_COMPLETE_MS (2) #define I2C_WAIT_TX_FIFO_EMPTY (1000) #define I2C_WAIT_ASYNC_TIMEOUT_US (6000000) /* I2Cx_CTL */ #define I2C_CTL_GRAS (0x1 << 0) #define I2C_CTL_GRAS_ACK (0) #define I2C_CTL_GRAS_NACK I2C_CTL_GRAS #define I2C_CTL_RB (0x1 << 1) #define I2C_CTL_GBCC_MASK (0x3 << 2) #define I2C_CTL_GBCC(x) (((x) & 0x3) << 2) #define I2C_CTL_GBCC_NONE I2C_CTL_GBCC(0) #define I2C_CTL_GBCC_START I2C_CTL_GBCC(1) #define I2C_CTL_GBCC_STOP I2C_CTL_GBCC(2) #define I2C_CTL_GBCC_RESTART I2C_CTL_GBCC(3) #define I2C_CTL_EN (0x1 << 5) #define I2C_CTL_IRQE (0x1 << 6) #define I2C_CTL_BUSSEL (1 << 7) #define I2C_CTL_DRQRE (1 << 8) #define I2C_CTL_DRQTE (1 << 9) #define I2C_CTL_RX_IRQ_THREHOLD_4BYTES (0x1 << 10) #define I2C_CTL_ADM_IRQ_EN (0x1 << 16) #define I2C_CTL_NACK_IRQ_EN (0x1 << 17) #define I2C_CTL_STD_IRQ_EN (0x1 << 19) #define I2C_CTL_FIFO_IRQ_EN (0x1 << 20) /* I2Cx_CLKDIV */ #define I2C_CLKDIV_DIV_MASK (0xff << 0) #define I2C_CLKDIV_DIV(x) (((x) & 0xff) << 0) /* I2Cx_STAT */ #define I2C_STAT_RACK (0x1 << 0) #define I2C_STAT_BEB (0x1 << 1) #define I2C_STAT_IRQP (0x1 << 2) #define I2C_STAT_STPD (0x1 << 4) #define I2C_STAT_STAD (0x1 << 5) #define I2C_STAT_BBB (0x1 << 6) #define I2C_STAT_TCB (0x1 << 7) #define I2C_STAT_LBST (0x1 << 8) #define I2C_STAT_SAMB (0x1 << 9) #define I2C_STAT_SRGC (0x1 << 10) /* I2Cx_CMD */ #define I2C_CMD_SBE (0x1 << 0) #define I2C_CMD_AS_MASK (0x7 << 1) #define I2C_CMD_AS(x) (((x) & 0x7) << 1) #define I2C_CMD_RBE (0x1 << 4) #define I2C_CMD_SAS_MASK (0x7 << 5) #define I2C_CMD_SAS(x) (((x) & 0x7) << 5) #define I2C_CMD_DE (0x1 << 8) #define I2C_CMD_NS (0x1 << 9) #define I2C_CMD_SE (0x1 << 10) #define I2C_CMD_MSS (0x1 << 11) #define I2C_CMD_WRS (0x1 << 12) #define I2C_CMD_EXEC (0x1 << 15) /* I2Cx_FIFOCTL */ #define I2C_FIFOCTL_NIB (0x1 << 0) #define I2C_FIFOCTL_RFR (0x1 << 1) #define I2C_FIFOCTL_TFR (0x1 << 2) /* I2Cx_FIFOSTAT */ #define I2C_FIFOSTAT_CECB (0x1 << 0) #define I2C_FIFOSTAT_RNB (0x1 << 1) #define I2C_FIFOSTAT_RFE (0x1 << 2) #define I2C_FIFOSTAT_RFF (0x1 << 3) #define I2C_FIFOSTAT_TFE (0x1 << 4) #define I2C_FIFOSTAT_TFF (0x1 << 5) #define I2C_FIFOSTAT_WRS (0x1 << 6) #define I2C_FIFOSTAT_RFD_MASK (0xf << 8) #define I2C_FIFOSTAT_RFD_SHIFT (8) #define I2C_FIFOSTAT_TFD_MASK (0xf << 12) #define I2C_FIFOSTAT_TFD_SHIFT (12) /* extract fifo level from fifostat */ #define I2C_RX_FIFO_LEVEL(x) (((x) >> 8) & 0xff) #define I2C_TX_FIFO_LEVEL(x) (((x) >> 12) & 0xff) enum i2c_state { STATE_INVALID, STATE_READ_DATA, STATE_WRITE_DATA, STATE_TRANSFER_OVER, STATE_TRANSFER_ERROR, }; /* I2C controller */ struct i2c_acts_controller { volatile uint32_t ctl; volatile uint32_t clkdiv; volatile uint32_t stat; volatile uint32_t addr; volatile uint32_t txdat; volatile uint32_t rxdat; volatile uint32_t cmd; volatile uint32_t fifoctl; volatile uint32_t fifostat; volatile uint32_t datcnt; volatile uint32_t rcnt; }; struct acts_i2c_config { struct i2c_acts_controller *base; void (*irq_config_func)(void); const char *dma_dev_name; uint8_t clock_id; uint8_t reset_id; uint8_t irq_id; uint8_t dma_id; uint8_t dma_chan; uint8_t use_dma; //not 0, use dma tranfser uint8_t use_cmd; //not 0, use cmd tranfser uint32_t clk_freq; }; /* Device run time data */ struct acts_i2c_data { struct k_mutex mutex; struct k_sem complete_sem; struct i2c_msg *cur_msg; uint32_t msg_buf_ptr; enum i2c_state state; uint32_t clk_freq; #ifdef CONFIG_I2C_SLAVE bool master_active; uint32_t status; struct i2c_slave_config *slave_cfg; bool slave_attached; #endif #ifdef CONFIG_DEVICE_POWER_MANAGEMENT uint32_t device_power_state; #endif #ifdef CONFIG_I2C_ASYNC cbuf_t cbuf; struct i2c_msg_async cur_msg_async; #ifdef CONFIG_I2C_ASYNC_MSG_INTERNAL_BUFFER uint8_t *rx_sync_buf; #endif uint8_t on_irq_async_msg : 1; /* If 1 to indicate that irq is on handling async messages */ uint8_t cur_msg_async_valid : 1; /* If 1 to indicate that the current async message is valid */ #endif }; #define DEV_NAME(dev) ((dev)->name) #define DEV_CFG(dev) \ ((const struct acts_i2c_config *const)(dev)->config) #define DEV_DATA(dev) \ ((struct acts_i2c_data *const)(dev)->data) #ifdef CONFIG_I2C_ASYNC #if IS_ENABLED(CONFIG_I2C_0) #define I2C0_RINGBUF_SIZE (CONFIG_I2C_0_MAX_ASYNC_ITEMS * sizeof(struct i2c_msg_async)) static uint8_t i2c0_ringbuf[I2C0_RINGBUF_SIZE]; #endif #if IS_ENABLED(CONFIG_I2C_1) #define I2C1_RINGBUF_SIZE (CONFIG_I2C_1_MAX_ASYNC_ITEMS * sizeof(struct i2c_msg_async)) static uint8_t i2c1_ringbuf[I2C1_RINGBUF_SIZE]; #endif #endif static void i2c_acts_dump_regs(struct i2c_acts_controller *i2c) { LOG_INF("I2C base 0x%x:\n" \ " ctl: %x clkdiv: %x stat: %x\n" \ " addr: %x cmd: %x fifoctl: %x\n" \ " fifostat: %x datcnt: %x rcnt: %x\n", (unsigned int)i2c, i2c->ctl, i2c->clkdiv, i2c->stat, i2c->addr, i2c->cmd, i2c->fifoctl, i2c->fifostat, i2c->datcnt, i2c->rcnt); } static void i2c_acts_set_clk(struct i2c_acts_controller *i2c, uint32_t clk_freq) { uint32_t div; uint32_t pclk_freq = CONFIG_HOSC_CLK_MHZ*1000000; if ((pclk_freq == 0) || (clk_freq == 0)) return; div = (pclk_freq + clk_freq * 16 - 1) / (clk_freq * 16); i2c->clkdiv = I2C_CLKDIV_DIV(div); return; } static int i2c_acts_configure(const struct device *dev, uint32_t config) { const struct acts_i2c_config *cfg = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = cfg->base; uint32_t bitrate; if (!(config & I2C_MODE_MASTER)) { LOG_ERR("Master Mode is not enabled"); return -EIO; } if (config & I2C_ADDR_10_BITS) { LOG_ERR("I2C 10-bit addressing is currently not supported"); LOG_ERR("Please submit a patch"); return -EIO; } /* Configure clock */ switch (I2C_SPEED_GET(config)) { case I2C_SPEED_STANDARD: bitrate = 100000U; break; case I2C_SPEED_FAST: bitrate = 400000U; break; case I2C_SPEED_FAST_PLUS: bitrate = 1000000U; break; case I2C_SPEED_HIGH: case I2C_SPEED_ULTRA: bitrate = 1500000U; break; default: LOG_ERR("Unsupported I2C speed value"); return -EIO; } /* Setup clock waveform */ i2c_acts_set_clk(i2c, bitrate); data->clk_freq = bitrate; return 0; } static void i2c_acts_reset(struct i2c_acts_controller *i2c) { /* reenable i2c controller */ i2c->ctl = 0; /* clear i2c status */ i2c->stat = 0xff; /* clear i2c fifo status */ i2c->fifoctl = I2C_FIFOCTL_RFR | I2C_FIFOCTL_TFR; /* wait until fifo reset complete */ while(i2c->fifoctl & (I2C_FIFOCTL_RFR | I2C_FIFOCTL_TFR)) ; } static int i2c_acts_wait_complete(struct i2c_acts_controller *i2c, uint32_t timeout_ms, bool is_read) { uint32_t start_time, curr_time; int i = 0; start_time = k_cycle_get_32(); while (!(i2c->fifostat & I2C_FIFOSTAT_CECB)) { curr_time = k_cycle_get_32(); if (k_cyc_to_us_floor32(curr_time - start_time) >= (timeout_ms * 1000)) { LOG_ERR("wait i2c cmd done timeout"); return -ETIMEDOUT; } } /* wait data really output to device */ if(!is_read){ for (i = 0; i < I2C_WAIT_TX_FIFO_EMPTY; i++) { if (i2c->fifostat & I2C_FIFOSTAT_TFE) break; k_busy_wait(1); } } if (i == I2C_WAIT_TX_FIFO_EMPTY) { LOG_ERR("wait i2c tx fifo:0x%x empty timeout", i2c->fifostat); return -ETIMEDOUT; } return 0; } #if defined(CONFIG_I2C_SLAVE) static void i2c_slave_acts_isr(struct device *dev); #endif static int __i2c_acts_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { const struct acts_i2c_config *config = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = config->base; struct i2c_msg *msg ; int i, is_read, ret = 0; uint32_t fifo_cmd; /* fifostat */ if (!num_msgs) return 0; #if defined(CONFIG_I2C_SLAVE) data->master_active = true; #endif i2c_acts_reset(i2c); /* enable I2C controller IRQ */ i2c->ctl = I2C_CTL_IRQE | I2C_CTL_EN | I2C_CTL_STD_IRQ_EN; fifo_cmd = I2C_CMD_EXEC | I2C_CMD_MSS | I2C_CMD_SE | I2C_CMD_DE | I2C_CMD_NS | I2C_CMD_SBE; if (num_msgs == 2) { /* set internal address and restart cmd for read operation */ fifo_cmd |= I2C_CMD_AS(msgs[0].len + 1) | I2C_CMD_SAS(1); /* write i2c device address */ i2c->txdat = (addr << 1); /* write internal register address */ for (i = 0; i < msgs[0].len; i++) i2c->txdat = msgs[0].buf[i]; msg = &msgs[1]; /* restart flag */ if (msg->flags & I2C_MSG_RESTART) { fifo_cmd |= I2C_CMD_RBE; } } else { /* only send device addess for 1 message */ fifo_cmd |= I2C_CMD_AS(1); msg = &msgs[0]; } data->cur_msg = msg; data->msg_buf_ptr = 0; LOG_DBG("msg flags:0x%x addr:0x%x buf:%p len:0x%x num_msgs:%d cur_msg:%p", msg->flags, addr, msg->buf, msg->len, num_msgs, data->cur_msg); /* set data count for the message */ i2c->datcnt = msg->len; is_read = ((msg->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) ? 1 : 0; if (is_read) { /* read from device, with WR bit */ i2c->txdat = (addr << 1) | 1; data->state = STATE_READ_DATA; } else { /* write to device */ if ((num_msgs == 1) || (msg->flags & I2C_MSG_RESTART)) { i2c->txdat = (addr << 1); } /* Write data to FIFO */ for (i = 0; i < msg->len; i++) { if (i2c->fifostat & I2C_FIFOSTAT_TFF) break; i2c->txdat = msg->buf[i]; } data->msg_buf_ptr = i; data->state = STATE_WRITE_DATA; } i2c->fifoctl = 0; /* write fifo command to start transfer */ i2c->cmd = fifo_cmd; return ret; } static void i2c_acts_update_cur_async_msg(const struct device *dev, struct i2c_msg_async *src_msg) { struct acts_i2c_data *data = DEV_DATA(dev); memcpy(&data->cur_msg_async, src_msg, sizeof(struct i2c_msg_async)); #ifdef CONFIG_I2C_ASYNC_MSG_INTERNAL_BUFFER uint8_t i; for (i = 0; i < data->cur_msg_async.num_msgs; i++) { if (data->cur_msg_async.msg[i].flags & I2C_MSG_READ) { memset(data->cur_msg_async.rx_buf, 0, sizeof(data->cur_msg_async.rx_buf)); data->cur_msg_async.msg[i].buf = data->cur_msg_async.rx_buf; } else { memcpy(data->cur_msg_async.tx_buf, data->cur_msg_async.msg[i].buf, data->cur_msg_async.msg[i].len); data->cur_msg_async.msg[i].buf = data->cur_msg_async.tx_buf; } } #endif data->cur_msg_async_valid = 1; } void i2c_acts_isr(void *arg) { struct device *dev = (struct device *)arg; const struct acts_i2c_config *config = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = config->base; struct i2c_msg *cur_msg = data->cur_msg; uint32_t stat, fifostat; int ret; LOG_DBG("stat:0x%x fifostat:0x%x state:%d", i2c->stat, i2c->fifostat, data->state); #if defined(CONFIG_I2C_SLAVE) if (data->slave_attached && !data->master_active) { i2c_slave_acts_isr(dev); return; } #endif stat = i2c->stat; fifostat = i2c->fifostat; /* check error */ if (fifostat & I2C_FIFOSTAT_RNB) { LOG_ERR("no ACK from device"); data->state = STATE_TRANSFER_ERROR; goto stop; } else if (stat & I2C_STAT_BEB) { LOG_ERR("bus error"); data->state = STATE_TRANSFER_ERROR; goto stop; } LOG_DBG("msg_buf_ptr:%d cur_msg:%p len:%d", data->msg_buf_ptr, cur_msg, cur_msg->len); if (data->state == STATE_READ_DATA) { /* read data from FIFO */ while ((!(i2c->fifostat & I2C_FIFOSTAT_RFE)) && data->msg_buf_ptr < cur_msg->len) { cur_msg->buf[data->msg_buf_ptr++] = i2c->rxdat; } /* all data is transfered? */ if (data->msg_buf_ptr >= cur_msg->len) { data->state = STATE_TRANSFER_OVER; } } else { /* all data is transfered? */ if (data->msg_buf_ptr >= cur_msg->len) { data->state = STATE_TRANSFER_OVER; } /* write data to FIFO */ while (!(i2c->fifostat & I2C_FIFOSTAT_TFF) && data->msg_buf_ptr < cur_msg->len) { i2c->txdat = cur_msg->buf[data->msg_buf_ptr++]; /* wait fifo stat is updated */ fifostat = i2c->fifostat; } } stop: i2c->stat |= I2C_STAT_IRQP; if (data->state == STATE_TRANSFER_ERROR || data->state == STATE_TRANSFER_OVER) { /* FIXME: add extra 2 bytes for TX to generate IRQ */ if (i2c->datcnt != cur_msg->len) { i2c->datcnt = cur_msg->len; } ret = i2c_acts_wait_complete(i2c, I2C_WAIT_COMPLETE_MS, ((cur_msg->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) ? true : false); if (ret) data->state = STATE_TRANSFER_ERROR; /* disable i2c controller */ i2c->ctl = 0; if (data->cur_msg_async.async_func) { data->cur_msg_async.async_func(data->cur_msg_async.cb_data, data->cur_msg_async.msg, data->cur_msg_async.num_msgs, (data->state == STATE_TRANSFER_ERROR)? true : false); } data->cur_msg_async_valid = 0; /* read next async message */ struct i2c_msg_async msg_async = {0}; ret = cbuf_read(&data->cbuf, &msg_async, sizeof(struct i2c_msg_async)); if (ret > 0) { LOG_DBG("read cbuf msg addr:0x%x", msg_async.addr); data->on_irq_async_msg = 1; i2c_acts_update_cur_async_msg(dev, &msg_async); __i2c_acts_transfer(dev, data->cur_msg_async.msg, data->cur_msg_async.num_msgs, data->cur_msg_async.addr); } else { cbuf_reset(&data->cbuf); data->on_irq_async_msg = 0; #if defined(CONFIG_I2C_SLAVE) data->master_active = false; #endif } } } static int i2c_acts_transfer_async(const struct device *dev, struct i2c_msg_async *msg_async) { struct acts_i2c_data *data = DEV_DATA(dev); const struct acts_i2c_config *config = DEV_CFG(dev); struct i2c_acts_controller *i2c = config->base; int ret, flags; bool need_start_flag = false; if ((!msg_async->num_msgs)|| (msg_async->num_msgs > 2)) { LOG_ERR("invalid msg number:%d", msg_async->num_msgs); return -EINVAL; } flags = irq_lock(); if ((!data->on_irq_async_msg) && (!data->cur_msg_async_valid)) need_start_flag = 1; if (need_start_flag) { i2c_acts_update_cur_async_msg(dev, msg_async); ret = __i2c_acts_transfer(dev, data->cur_msg_async.msg, data->cur_msg_async.num_msgs, data->cur_msg_async.addr); } else { LOG_DBG("write msg addr:0x%x to cbuf", msg_async->addr); ret = cbuf_write(&data->cbuf, msg_async, sizeof(struct i2c_msg_async)); if (!ret) { LOG_ERR("write cbuf error(%d, %d)", data->on_irq_async_msg, data->cur_msg_async_valid); i2c_acts_dump_regs(i2c); i2c_acts_reset(i2c); cbuf_reset(&data->cbuf); data->cur_msg_async_valid = 0; data->on_irq_async_msg = 0; ret = -EAGAIN; } else { ret = 0; } } irq_unlock(flags); return ret; } static int i2c_acts_async_dummy_cb(void *cb_data, struct i2c_msg *msgs, uint8_t num_msgs, bool is_err) { const struct device *dev = (const struct device *)cb_data; struct acts_i2c_data *data = DEV_DATA(dev); #ifdef CONFIG_I2C_ASYNC_MSG_INTERNAL_BUFFER if (data->rx_sync_buf && data->cur_msg_async_valid) { uint8_t i; for (i = 0; i < data->cur_msg_async.num_msgs; i++) { if (data->cur_msg_async.msg[i].flags & I2C_MSG_READ) { memcpy(data->rx_sync_buf, data->cur_msg_async.msg[i].buf, data->cur_msg_async.msg[i].len); break; } } } #endif k_sem_give(&data->complete_sem); return 0; } static int i2c_acts_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { const struct acts_i2c_config *config = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = config->base; int i = 0, ret = 0; uint32_t start_time; struct i2c_msg_async msg_async = {0}; if ((!num_msgs) || (num_msgs > 2)) { LOG_ERR("invalid num_msgs:%d", num_msgs); return -EINVAL; } msg_async.num_msgs = num_msgs; msg_async.async_func = i2c_acts_async_dummy_cb; msg_async.cb_data = (void *)dev; msg_async.addr = addr; for (i = 0; i < num_msgs; i++) memcpy(&msg_async.msg[i], &msgs[i], sizeof(struct i2c_msg)); k_mutex_lock(&data->mutex, K_FOREVER); #ifdef CONFIG_I2C_ASYNC_MSG_INTERNAL_BUFFER for (i = 0; i < num_msgs; i++) { if (msgs[i].flags & I2C_MSG_READ) { data->rx_sync_buf = msgs[i].buf; break; } } #endif /* wait async messages finished */ start_time = k_cycle_get_32(); while (data->cbuf.length > 0) { if (k_cyc_to_us_floor32(k_cycle_get_32() - start_time) >= I2C_WAIT_ASYNC_TIMEOUT_US) { LOG_ERR("wait async timeout"); k_mutex_unlock(&data->mutex); return -ETIMEDOUT; } } ret = i2c_acts_transfer_async(dev, &msg_async); if (ret) { LOG_ERR("i2c async error:%d", ret); i2c_acts_dump_regs(i2c); goto out; } ret = k_sem_take(&data->complete_sem, I2C_TIMEOUT_MS); if (ret) { /* wait timeout */ LOG_ERR("addr 0x%x: wait timeout", addr); ret = -ETIMEDOUT; } if (data->state == STATE_TRANSFER_ERROR) { LOG_ERR("addr 0x%x: transfer error", addr); i2c_acts_dump_regs(i2c); ret = -EIO; } #ifdef CONFIG_I2C_ASYNC_MSG_INTERNAL_BUFFER data->rx_sync_buf = NULL; #endif out: if (ret) { i2c_acts_dump_regs(i2c); #ifdef CONFIG_I2C_ASYNC data->cur_msg_async_valid = 0; cbuf_reset(&data->cbuf); data->on_irq_async_msg = 0; #endif } k_mutex_unlock(&data->mutex); return ret; } int i2c_acts_init(const struct device *dev) { const struct acts_i2c_config *config = DEV_CFG(dev);; struct acts_i2c_data *data = DEV_DATA(dev); /* enable i2c controller clock */ acts_clock_peripheral_enable(config->clock_id); /* reset i2c controller */ acts_reset_peripheral(config->reset_id); /* setup default clock to 100K */ i2c_acts_set_clk(config->base, config->clk_freq); printk("i2c%d:clk=%d,cmd=%d,dma=%d\n",config->clock_id-CLOCK_ID_I2C0, config->clk_freq, config->use_cmd, config->use_dma); k_mutex_init(&data->mutex); k_sem_init(&data->complete_sem, 0, UINT_MAX); #if IS_ENABLED(CONFIG_I2C_0) if ((uint32_t)config->base == I2C0_REG_BASE) cbuf_init(&data->cbuf, i2c0_ringbuf, sizeof(i2c0_ringbuf)); #endif #if IS_ENABLED(CONFIG_I2C_1) if ((uint32_t)config->base == I2C1_REG_BASE) cbuf_init(&data->cbuf, i2c1_ringbuf, sizeof(i2c1_ringbuf)); #endif data->on_irq_async_msg = 0; config->irq_config_func(); return 0; } #if defined(CONFIG_I2C_SLAVE) #define I2C_SLAVE_STATUS_IDLE 0 #define I2C_SLAVE_STATUS_READY 1 #define I2C_SLAVE_STATUS_START 2 #define I2C_SLAVE_STATUS_ADDRESS 3 #define I2C_SLAVE_STATUS_MASTER_WRITE 4 #define I2C_SLAVE_STATUS_MASTER_READ 5 #define I2C_SLAVE_STATUS_STOPED 6 #define I2C_SLAVE_STATUS_ERR 15 static void i2c_slave_acts_isr(struct device *dev) { const struct acts_i2c_config *cfg = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = cfg->base; const struct i2c_slave_callbacks *cf= data->slave_cfg->callbacks; uint32_t stat; uint16_t addr; uint8_t val; stat = i2c->stat; /* clear pending */ i2c->stat = I2C_STAT_IRQP; /* check error */ if (stat & I2C_STAT_BEB) { LOG_ERR("bus error\n"); i2c_acts_dump_regs(i2c); i2c_acts_reset(i2c); data->status = I2C_SLAVE_STATUS_ERR; goto out; } /* detected start signal */ if (stat & I2C_STAT_STAD) { i2c->stat = I2C_STAT_STAD; data->status = I2C_SLAVE_STATUS_START; } /* recieved address or data */ if (stat & I2C_STAT_TCB) { i2c->stat = I2C_STAT_TCB; if (!(i2c->stat & I2C_STAT_LBST)) { /* receive address */ data->status = I2C_SLAVE_STATUS_ADDRESS; addr = i2c->rxdat; if ((addr >> 1) != data->slave_cfg->address) { LOG_ERR("bus address (0x%x) not matched with (0x%x)\n", addr >> 1, data->slave_cfg->address); i2c->stat |= 0x1ff; goto out; } if (addr & 1) { /* master read */ cf->read_requested(data->slave_cfg, &val); i2c->txdat = val; data->status = I2C_SLAVE_STATUS_MASTER_READ; } else { /* master write */ i2c->ctl &= ~I2C_CTL_GRAS_ACK; cf->write_requested(data->slave_cfg); data->status = I2C_SLAVE_STATUS_MASTER_WRITE; } } else { /* receive data */ if (data->status == I2C_SLAVE_STATUS_MASTER_READ) { /* master <--- slave */ if (!(stat & I2C_STAT_RACK)) { /* received NACK */ goto out; } cf->read_processed(data->slave_cfg, &val); i2c->txdat = val; } else { /* master ---> slave */ val = i2c->rxdat; if(!cf->write_received(data->slave_cfg, val)) i2c->ctl |= I2C_CTL_GRAS_ACK; } } } /* detected stop signal */ if (stat & I2C_STAT_STPD) { i2c->stat = I2C_STAT_STPD; cf->stop(data->slave_cfg); i2c->ctl &= ~I2C_CTL_GRAS_ACK; } out: i2c->ctl |= I2C_CTL_RB; } /* Attach and start I2C as slave */ int i2c_acts_slave_register(struct device *dev, struct i2c_slave_config *config) { const struct acts_i2c_config *cfg = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = cfg->base; if (!config) { return -EINVAL; } if (data->slave_attached) { LOG_ERR("i2c: err slave is registered\n"); return -EBUSY; } if (data->master_active) { LOG_ERR("i2c: master is transfer\n"); return -EBUSY; } i2c->addr = config->address << 1; data->slave_cfg = config; data->slave_attached = true; LOG_DBG("i2c: slave registered"); i2c->ctl = I2C_CTL_IRQE | I2C_CTL_EN | I2C_CTL_RB | I2C_CTL_GRAS_ACK | I2C_CTL_STD_IRQ_EN; return 0; } int i2c_acts_slave_unregister(struct device *dev, struct i2c_slave_config *config) { const struct acts_i2c_config *cfg = DEV_CFG(dev); struct acts_i2c_data *data = DEV_DATA(dev); struct i2c_acts_controller *i2c = cfg->base; if (!data->slave_attached) { return -EINVAL; } if (data->master_active) { return -EBUSY; } data->slave_cfg = NULL; /* disable i2c controller */ i2c->ctl = 0; data->slave_attached = false; LOG_DBG("i2c: slave unregistered"); return 0; } #endif /* defined(CONFIG_I2C_SLAVE) */ const struct i2c_driver_api i2c_acts_driver_api = { .configure = i2c_acts_configure, .transfer = i2c_acts_transfer, #if defined(CONFIG_I2C_SLAVE) .slave_register = i2c_acts_slave_register, .slave_unregister = i2c_acts_slave_unregister, #endif #if defined(CONFIG_I2C_ASYNC) .transfer_async = i2c_acts_transfer_async, #endif }; #ifdef CONFIG_DEVICE_POWER_MANAGEMENT_NOT_USE static void i2c_acts_set_power_state(struct device *dev, uint32_t power_state) { struct acts_i2c_data *drv_data = dev->data; drv_data->device_power_state = power_state; } static uint32_t i2c_acts_get_power_state(struct device *dev) { struct acts_i2c_data *drv_data = dev->data; return drv_data->device_power_state; } static int i2c_suspend_device(struct device *dev) { if (device_busy_check(dev)) { return -EBUSY; } i2c_acts_set_power_state(dev, DEVICE_PM_SUSPEND_STATE); return 0; } static int i2c_resume_device_from_suspend(struct device *dev) { i2c_acts_set_power_state(dev, DEVICE_PM_ACTIVE_STATE); return 0; } /* * Implements the driver control management functionality * the *context may include IN data or/and OUT data */ int i2c_device_ctrl(struct device *dev, uint32_t ctrl_command, void *context) { if (ctrl_command == DEVICE_PM_SET_POWER_STATE) { if (*((uint32_t *)context) == DEVICE_PM_SUSPEND_STATE) { return i2c_suspend_device(dev); } else if (*((uint32_t *)context) == DEVICE_PM_ACTIVE_STATE) { return i2c_resume_device_from_suspend(dev); } } else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) { *((uint32_t *)context) = i2c_acts_get_power_state(dev); return 0; } return 0; } #else #define i2c_acts_set_power_state(...) #endif /* CONFIG_DEVICE_POWER_MANAGEMENT */ #define dma_use(n) (\ .dma_dev_name = CONFIG_DMA_0_NAME, \ .dma_id = CONFIG_I2C_##n##_DMA_ID,\ .dma_chan = CONFIG_I2C_##n##_DMA_CHAN,\ .use_dma = 1, \ ) #define dma_not(n) (\ .use_dma = 0, \ ) // COND_CODE_1(CONFIG_I2C_##n##_USE_DMA,dma_use(n), dma_not(n)) #define I2C_ACTS_INIT(n) \ static const struct device DEVICE_NAME_GET(i2c##n##_acts); \ \ static void i2c##n##_acts_irq_config(void) \ { \ IRQ_CONNECT(IRQ_ID_I2C##n, CONFIG_I2C_##n##_IRQ_PRI, \ i2c_acts_isr, \ DEVICE_GET(i2c##n##_acts), 0); \ irq_enable(IRQ_ID_I2C##n); \ } \ \ static const struct acts_i2c_config i2c##n##_acts_config = { \ .base = (struct i2c_acts_controller *)I2C##n##_REG_BASE, \ .irq_config_func = i2c##n##_acts_irq_config, \ .clock_id = CLOCK_ID_I2C##n,\ .reset_id = RESET_ID_I2C##n,\ COND_DMA_CODE(CONFIG_I2C_##n##_USE_DMA, CONFIG_I2C_##n##_DMA_ID, CONFIG_I2C_##n##_DMA_CHAN) \ .use_cmd = 0,\ .irq_id = IRQ_ID_I2C##n, \ .clk_freq = CONFIG_I2C_##n##_CLK_FREQ, \ }; \ \ static struct acts_i2c_data i2c##n##_acts_data; \ \ DEVICE_DEFINE(i2c##n##_acts, CONFIG_I2C_##n##_NAME, \ &i2c_acts_init, NULL, \ &i2c##n##_acts_data, &i2c##n##_acts_config, \ POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ &i2c_acts_driver_api); #if IS_ENABLED(CONFIG_I2C_0) I2C_ACTS_INIT(0) #endif #if IS_ENABLED(CONFIG_I2C_1) I2C_ACTS_INIT(1) #endif