349 lines
12 KiB
C
349 lines
12 KiB
C
/**
|
|
* @file sdrv_i2c_slave.c
|
|
* @brief sdrv i2c slave driver function file.
|
|
*
|
|
* @Copyright (c) 2022 Semidrive Semiconductor.
|
|
* @All rights reserved.
|
|
**/
|
|
|
|
#include <debug.h>
|
|
#include <reg.h>
|
|
#include <sdrv_i2c.h>
|
|
#include <udelay/udelay.h>
|
|
|
|
#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);
|
|
} |