/** * @file sdrv_i2c_slave.c * @brief sdrv i2c slave driver function file. * * @Copyright (c) 2022 Semidrive Semiconductor. * @All rights reserved. **/ #include #include #include #include #include "irq.h" #include "sdrv_i2c_reg.h" #define i2c_min(a, b) ((a) < (b) ? a : b) /* No ACK from slave */ #define I2C_SLAVE_NACK 0x27 static void sdrv_i2c_slave_reset(sdrv_i2c_t *ctrl) { sdrv_i2c_cfg_t *cfg = ctrl->cfg; sdrv_i2cdrv_sctrl_t *sctrl = &ctrl->sctrl; i2c_slave_transfer_t *transfer = &sctrl->transfer; if (cfg->speed != SDRV_I2CDRV_USPEED) { sdrv_i2c_unmask_int(ctrl->cfg->base, I2C_INTEN0, SDRV_I2C_INT0_SLV_DEFAULT_MASK); } sdrv_i2c_clear_fifo(cfg->base); sdrv_i2c_clear_int(cfg->base); sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR1, SDRV_I2C_CMD1_SLV_MASK); if (cfg->speed != SDRV_I2CDRV_USPEED) { sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR2, SDRV_I2C_CMD2_SLV_MASK); } sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR3, SDRV_I2C_CMD3_SLV_MASK); transfer->event = I2C_SLV_NONE_EVENT; transfer->buffer = NULL; transfer->buffer_bytes = 0; transfer->transferred_bytes = 0; transfer->status = SDRV_I2CDRV_TRANS_OK; } static int sdrv_i2c_slave_irq_handler(uint32_t irq, void *arg) { uint32_t int0_stat, int2_stat, cmdcsr2, inten0, fifo_size; sdrv_i2c_t *ctrl = arg; sdrv_i2cdrv_sctrl_t *sctrl = &ctrl->sctrl; i2c_slave_transfer_t *transfer = &sctrl->transfer; uint32_t exception_handle = 0; sdrv_i2c_cfg_t *cfg = ctrl->cfg; int32_t hw_avail = 0, avail = 0, remaining_bytes = 0; int0_stat = sdrv_i2c_get_int_stat(cfg->base, I2C_INTR0); int2_stat = sdrv_i2c_get_int_stat(cfg->base, I2C_INTR2); inten0 = readl(cfg->base + I2C_INTEN0); exception_handle = (int2_stat & SDRV_I2C_INT2_STOPDET) | (int0_stat & (SDRV_I2C_INT0_SLVWRTRANSABORT | SDRV_I2C_INT0_SLVRDTRANSABORT)); if (exception_handle) { if (transfer->event == I2C_SLV_RECEIVE_EVENT) { if (NULL != transfer->buffer) { hw_avail = sdrv_i2c_get_rspace(cfg->base); remaining_bytes = transfer->buffer_bytes - transfer->transferred_bytes; if (remaining_bytes >= hw_avail) { while (hw_avail--) { transfer->buffer[transfer->transferred_bytes++] = sdrv_i2c_recv_data(cfg->base); } } } } if (transfer->event == I2C_SLV_TRANSMIT_EVENT) { fifo_size = (readl(cfg->base + I2C_FSR) >> 16) & 0XFF; hw_avail = sdrv_i2c_get_wspace(cfg->base); transfer->transferred_bytes -= fifo_size - hw_avail; } transfer->event = I2C_SLV_STOP_EVENT; transfer->status = SDRV_I2CDRV_TRANS_OK; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_slave_reset(ctrl); return 0; } if (int0_stat & SDRV_I2C_INT0_SLV_ERR_STAT) { transfer->event = I2C_SLV_STOP_EVENT; transfer->status = SDRV_I2CDRV_TRANS_ERR; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_slave_reset(ctrl); ssdk_printf(SSDK_ERR, "slv i2c-%u err stat0=%x,stat2=%x\n", cfg->id, int0_stat, int2_stat); return 0; } if (int2_stat & SDRV_I2C_INT2_SLVRDREQDET) { sdrv_i2c_write_reg(cfg->base, I2C_INTR2, int2_stat & SDRV_I2C_INT2_SLVRDREQDET); transfer->transferred_bytes = 0; transfer->event = I2C_SLV_TRANSMIT_EVENT; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_set_cmdcsr(cfg->base, I2C_CMDCSR3, (transfer->buffer_bytes << 16) | transfer->slave_addr); sdrv_i2c_unmask_int(cfg->base, I2C_INTEN0, SDRV_I2C_INT0_SLV_DEFAULT_MASK | SDRV_I2C_INT0_TXFWE); } else if (int2_stat & SDRV_I2C_INT2_SLVWRREQDET) { sdrv_i2c_write_reg(cfg->base, I2C_INTR2, int2_stat & SDRV_I2C_INT2_SLVWRREQDET); transfer->transferred_bytes = 0; transfer->event = I2C_SLV_RECEIVE_EVENT; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_set_cmdcsr(cfg->base, I2C_CMDCSR2, (transfer->buffer_bytes << 16) | transfer->slave_addr); sdrv_i2c_unmask_int(cfg->base, I2C_INTEN0, SDRV_I2C_INT0_SLV_DEFAULT_MASK | SDRV_I2C_INT0_RXFWF); } if (int0_stat & SDRV_I2C_INT0_RXFWF) { if (transfer->event == I2C_SLV_RECEIVE_EVENT) { if (NULL == transfer->buffer) { ssdk_printf(SSDK_ERR, "slv i2c buffer null\n"); goto slave_err; } hw_avail = sdrv_i2c_get_rspace(cfg->base); remaining_bytes = transfer->buffer_bytes - transfer->transferred_bytes; if (remaining_bytes >= hw_avail) { while (hw_avail--) { transfer->buffer[transfer->transferred_bytes++] = sdrv_i2c_recv_data(cfg->base); } } } sdrv_i2c_write_reg(cfg->base, I2C_INTR0, int0_stat & SDRV_I2C_INT0_RXFWF); } if (int0_stat & SDRV_I2C_INT0_SLVWRTRANSDONE) { if (transfer->event == I2C_SLV_RECEIVE_EVENT) { if (NULL == transfer->buffer) { goto slave_err; } hw_avail = sdrv_i2c_get_rspace(cfg->base); remaining_bytes = transfer->buffer_bytes - transfer->transferred_bytes; if (remaining_bytes >= hw_avail) { while (hw_avail--) { transfer->buffer[transfer->transferred_bytes++] = sdrv_i2c_recv_data(cfg->base); } } transfer->status = SDRV_I2CDRV_TRANS_OK; transfer->event = I2C_SLV_COMPLETION_EVENT; if (sctrl->callback) sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_slave_reset(ctrl); sdrv_i2c_write_reg(cfg->base, I2C_INTR0, int0_stat & SDRV_I2C_INT0_SLVWRTRANSDONE); } if (transfer->event == I2C_SLV_TRANSMIT_EVENT) { if (int0_stat & SDRV_I2C_INT0_TXFWE) { sdrv_i2c_write_reg(cfg->base, I2C_INTR0, int0_stat & SDRV_I2C_INT0_TXFWE); if (NULL == transfer->buffer) { ssdk_printf(SSDK_ERR, "slv i2c buffer null\n"); goto slave_err; } if (transfer->transferred_bytes < transfer->buffer_bytes) { hw_avail = sdrv_i2c_get_wspace(cfg->base); remaining_bytes = transfer->buffer_bytes - transfer->transferred_bytes; avail = i2c_min(hw_avail, remaining_bytes); while (avail--) { sdrv_i2c_send_data( cfg->base, transfer->buffer[transfer->transferred_bytes++]); } } else { sdrv_i2c_unmask_int(cfg->base, I2C_INTEN0, SDRV_I2C_INT0_SLV_DEFAULT_MASK); } } if (int0_stat & SDRV_I2C_INT0_SLVRDTRANSDONE) { transfer->status = SDRV_I2CDRV_TRANS_OK; transfer->event = I2C_SLV_COMPLETION_EVENT; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_slave_reset(ctrl); } } sdrv_i2c_clear_int(cfg->base); return 0; slave_err: transfer->event = I2C_SLV_STOP_EVENT; transfer->status = SDRV_I2CDRV_TRANS_ERR; if (sctrl->callback) { sctrl->callback(ctrl, transfer, sctrl->cb_param); } sdrv_i2c_clear_int(cfg->base); sdrv_i2c_slave_reset(ctrl); return 0; } status_t sdrv_i2c_slave_transfer_create(sdrv_i2c_t *ctrl, i2c_slave_transfer_config_t *config) { sdrv_i2cdrv_sctrl_t *sctrl = &ctrl->sctrl; i2c_slave_transfer_t *transfer = &sctrl->transfer; ASSERT(ctrl != NULL); ASSERT(ctrl->cfg != NULL); ASSERT(ctrl->cfg->opmode == SDRV_I2CDRV_SLAVE); ASSERT(config != NULL); sctrl->callback = config->callback; sctrl->cb_param = config->cb_param; sdrv_i2c_slave_reset(ctrl); sdrv_i2c_set_opmode(ctrl->cfg->base, SDRV_I2C_SLAVE); transfer->slave_addr = sctrl->slave_addr; } /** * @brief Initializes the I2C slave module. * This function enables the peripheral clock and initializes the I2C slave * peripheral as described by the user provided configuration. * * @param ctrl Pointer to the I2C controller * @return status_t */ status_t sdrv_i2c_slave_init(sdrv_i2c_t *ctrl) { ASSERT(ctrl != NULL); ASSERT(ctrl->cfg != NULL); ASSERT(ctrl->cfg->opmode == SDRV_I2CDRV_SLAVE); /* when i2c work in slave mode ,must config salve device address */ if (0 == ctrl->cfg->slave_addr) { return SDRV_STATUS_INVALID_PARAM; } ctrl->sctrl.slave_addr = ctrl->cfg->slave_addr; /* Reset */ sdrv_i2c_disable_int(ctrl->cfg->base); sdrv_i2c_clear_fifo(ctrl->cfg->base); sdrv_i2c_clear_int(ctrl->cfg->base); sdrv_i2c_reset(ctrl->cfg->base); /* Set watermark */ sdrv_i2c_set_watermark(ctrl->cfg->base, SDRV_I2C_RXTX_WML); /* Set timing */ if (ctrl->cfg->speed == SDRV_I2CDRV_HSPEED) sdrv_i2c_set_speed(ctrl->cfg->base, ctrl->cfg->clk, SDRV_I2C_HSPEED); else if (ctrl->cfg->speed == SDRV_I2CDRV_USPEED) sdrv_i2c_set_speed(ctrl->cfg->base, ctrl->cfg->clk, SDRV_I2C_USPEED); else sdrv_i2c_set_speed(ctrl->cfg->base, ctrl->cfg->clk, SDRV_I2C_NSPEED); if (ctrl->cfg->speed == SDRV_I2CDRV_SSPEED) sdrv_i2c_set_timing(ctrl->cfg->base, ctrl->cfg->clk, 0); else if (ctrl->cfg->speed == SDRV_I2CDRV_FSPEED) sdrv_i2c_set_timing(ctrl->cfg->base, ctrl->cfg->clk, 1); else if (ctrl->cfg->speed == SDRV_I2CDRV_PSPEED) sdrv_i2c_set_timing(ctrl->cfg->base, ctrl->cfg->clk, 2); else if (ctrl->cfg->speed == SDRV_I2CDRV_HSPEED) sdrv_i2c_set_timing(ctrl->cfg->base, ctrl->cfg->clk, 3); else if (ctrl->cfg->speed == SDRV_I2CDRV_USPEED) sdrv_i2c_set_timing(ctrl->cfg->base, ctrl->cfg->clk, 4); /* Disable interrupts */ sdrv_i2c_disable_int(ctrl->cfg->base); /* Clear FIFO */ sdrv_i2c_clear_fifo(ctrl->cfg->base); /* Clear interrupt */ sdrv_i2c_clear_int(ctrl->cfg->base); sdrv_i2c_set_opmode(ctrl->cfg->base, SDRV_I2C_SLAVE); sdrv_i2c_set_slvaddr(ctrl->cfg->base, ctrl->sctrl.slave_addr); sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR1, SDRV_I2C_CMD1_SLV_MASK); sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR2, SDRV_I2C_CMD2_SLV_MASK); sdrv_i2c_set_cmdcsr(ctrl->cfg->base, I2C_CMDCSR3, SDRV_I2C_CMD3_SLV_MASK); sdrv_i2c_unmask_int(ctrl->cfg->base, I2C_INTEN0, SDRV_I2C_INT0_SLV_DEFAULT_MASK); sdrv_i2c_unmask_int(ctrl->cfg->base, I2C_INTEN2, SDRV_I2C_INT2_SLV_MASK); if (ctrl->cfg->irq) { irq_attach(ctrl->cfg->irq, sdrv_i2c_slave_irq_handler, ctrl); irq_enable(ctrl->cfg->irq); } return SDRV_STATUS_OK; } /** * @brief Deinitializes the I2C slave controller. * * @param ctrl Pointer to the I2C controller * @return status_t */ status_t sdrv_i2c_slave_deinit(sdrv_i2c_t *ctrl) { ASSERT(ctrl != NULL); ASSERT(ctrl->cfg != NULL); ASSERT(ctrl->cfg->opmode == SDRV_I2CDRV_SLAVE); sdrv_i2c_disable_int(ctrl->cfg->base); sdrv_i2c_clear_fifo(ctrl->cfg->base); sdrv_i2c_clear_int(ctrl->cfg->base); sdrv_i2c_reset(ctrl->cfg->base); if (ctrl->cfg->irq) { irq_disable(ctrl->cfg->irq); } /* Disable I2C */ sdrv_i2c_disable(ctrl->cfg->base); }