openmv/lib/alif/drivers/source/i3c.c
2025-10-13 09:45:16 -03:00

1515 lines
51 KiB
C

/* Copyright (C) 2023 Alif Semiconductor - All Rights Reserved.
* Use, distribution and modification of this code is permitted under the
* terms stated in the Alif Semiconductor Software License Agreement
*
* You should have received a copy of the Alif Semiconductor Software
* License Agreement with this file. If not, please write to:
* contact@alifsemi.com, or visit: https://alifsemi.com/license
*
*/
#include "i3c.h"
#include "string.h"
/**
\fn static void i3c_wr_tx_fifo(I3C_Type *i3c,
const uint8_t *bytes,
uint32_t nbytes)
\brief Write data to i3c TX FIFO
\param[in] i3c : Pointer to i3c resources structure
\param[in] bytes : Pointer to buffer with data which
needs to be write to i3c transmitter
\param[in] nbytes : Number of bytes needs to be write
\return none
*/
static void i3c_wr_tx_fifo(I3C_Type *i3c,
const uint8_t *bytes,
uint32_t nbytes)
{
uint32_t len_in_words = nbytes / 4;
uint32_t i, tmp;
/* FIXME Add check for MAX TX FIFO Length */
for (i = 0; i < len_in_words; i++)
{
/* FIXME Fix Memory Alignment issue */
tmp = *((uint32_t *) bytes);
i3c->I3C_TX_DATA_PORT = tmp;
bytes += 4;
}
/* write the remaining bytes in the last incomplete word */
if (nbytes & 3)
{
tmp = 0;
memcpy(&tmp, bytes, nbytes & 3);
i3c->I3C_TX_DATA_PORT = tmp;
}
}
/**
\fn static void i3c_read_rx_fifo(I3C_Type *i3c,
uint8_t *bytes,
uint32_t nbytes)
\brief Read data from i3c RX FIFO
\param[in] i3c : Pointer to i3c resources structure
\param[in] bytes : Pointer to buffer for data
to receive from i3c RX FIFO
\param[in] nbytes : Number of bytes needs to be receive
\return none
*/
static void i3c_read_rx_fifo(I3C_Type *i3c,
uint8_t *bytes,
uint32_t nbytes)
{
uint32_t len_in_words = nbytes / 4;
uint32_t i, tmp;
/* FIXME Add check for MAX RX FIFO Length */
for (i = 0; i < len_in_words; i++)
{
/* FIXME Fix Memory Alignment issue */
*((uint32_t *) bytes) = i3c->I3C_RX_DATA_PORT;
bytes += 4;
}
/* read the last word and copy the actual remaining data */
if (nbytes & 3)
{
tmp = i3c->I3C_RX_DATA_PORT;
memcpy(bytes, &tmp, nbytes & 3);
}
}
/**
\fn static void i3c_fetch_error_type(i3c_xfer_t *xfer)
\brief Fetches the error type
\param[in] xfer : Transfer data structure
\return none
*/
static void i3c_fetch_error_type(i3c_xfer_t *xfer)
{
switch(xfer->error)
{
case I3C_RESPONSE_QUEUE_PORT_ERR_CRC:
xfer->error = I3C_COMM_ERROR_CRC;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_PARITY:
xfer->error = I3C_COMM_ERROR_PARITY;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_FRAME:
xfer->error = I3C_COMM_ERROR_FRAME;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_IBA_NACK:
xfer->error = I3C_COMM_ERROR_IBA_NACK;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_ADDRESS_NACK:
xfer->error = I3C_COMM_ERROR_ADDR_NACK;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_OVER_UNDER_FLOW:
xfer->error = I3C_COMM_ERROR_BUF_UNDR_OVR_FLW;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_XFER_ABORT:
xfer->error = I3C_COMM_ERROR_XFER_ABORT;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_I2C_W_NACK:
xfer->error = I3C_COMM_ERROR_I2C_SLV_W_NACK;
break;
case I3C_RESPONSE_QUEUE_PORT_ERR_PEC_OR_EARLY_TERM:
xfer->error = I3C_COMM_ERROR_PEC_OR_EARLY_TERM;
break;
default:
break;
}
}
/**
\fn static void i3c_enqueue_xfer(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief Add commands to i3c Command Queue
\param[in] i3c : Pointer to i3c resources structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
static void i3c_enqueue_xfer(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t thld_ctrl;
/* write data to tx port (if any) */
if (xfer->tx_buf)
{
i3c_wr_tx_fifo(i3c, xfer->tx_buf, xfer->tx_len);
}
thld_ctrl = i3c->I3C_QUEUE_THLD_CTRL;
thld_ctrl &= ~I3C_QUEUE_THLD_CTRL_RESP_BUF_THLD_Msk;
/* set up for an interrupt after one response */
thld_ctrl |= I3C_QUEUE_THLD_CTRL_RESP_BUF_THLD(1);
i3c->I3C_QUEUE_THLD_CTRL = thld_ctrl;
if(xfer->xfer_cmd.cmd_hi)
{
i3c->I3C_COMMAND_QUEUE_PORT = xfer->xfer_cmd.cmd_hi;
}
if(xfer->xfer_cmd.cmd_lo)
{
i3c->I3C_COMMAND_QUEUE_PORT = xfer->xfer_cmd.cmd_lo;
}
}
/**
\fn static void i3c_dispatch_xfer_cmd(I3C_Type *i3c,
\ i3c_xfer_t *xfer)
\brief Add commands to i3c Command Queue
\param[in] i3c : Pointer to i3c resources structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
static void i3c_dispatch_xfer_cmd(I3C_Type *i3c, i3c_xfer_t *xfer)
{
if(xfer->xfer_cmd.cmd_type == I3C_XFER_TYPE_ADDR_ASSIGN)
{
/* Issue ccc command */
i3c->I3C_COMMAND_QUEUE_PORT =
I3C_COMMAND_QUEUE_PORT_SPEED(xfer->xfer_cmd.speed) |
I3C_COMMAND_QUEUE_PORT_DEV_INDEX(xfer->xfer_cmd.addr_index) |
I3C_COMMAND_QUEUE_PORT_CMD(xfer->xfer_cmd.cmd_id) |
I3C_COMMAND_QUEUE_PORT_TOC |
I3C_COMMAND_QUEUE_PORT_ROC |
I3C_COMMAND_QUEUE_PORT_DEV_COUNT(xfer->xfer_cmd.addr_depth) |
I3C_COMMAND_QUEUE_PORT_ADDR_ASSGN_CMD |
I3C_COMMAND_QUEUE_PORT_TID(xfer->xfer_cmd.port_id);
}
else
{
/* Perform the following if it is not Address command */
xfer->xfer_cmd.cmd_hi =
I3C_COMMAND_QUEUE_PORT_ARG_DATA_LEN(xfer->xfer_cmd.data_len) |
I3C_COMMAND_QUEUE_PORT_TRANSFER_ARG;
xfer->xfer_cmd.cmd_lo =
I3C_COMMAND_QUEUE_PORT_SPEED(xfer->xfer_cmd.speed) |
I3C_COMMAND_QUEUE_PORT_DEV_INDEX(xfer->xfer_cmd.addr_index) |
I3C_COMMAND_QUEUE_PORT_CMD(xfer->xfer_cmd.cmd_id) |
I3C_COMMAND_QUEUE_PORT_TOC |
I3C_COMMAND_QUEUE_PORT_TID(xfer->xfer_cmd.port_id) |
I3C_COMMAND_QUEUE_PORT_ROC |
I3C_COMMAND_QUEUE_PORT_PEC;
/* Add Command present macro if command is present or
* is a HDR-DDR communicatoin */
if((xfer->xfer_cmd.cmd_id) ||
(xfer->xfer_cmd.speed == I3C_SPEED_HDR_DDR))
{
xfer->xfer_cmd.cmd_lo |= I3C_COMMAND_QUEUE_PORT_CP;
/* Add Defining byte */
if(xfer->xfer_cmd.def_byte)
{
xfer->xfer_cmd.cmd_hi |= I3C_COMMAND_QUEUE_PORT_ARG_DATA_DB(
xfer->xfer_cmd.def_byte);
xfer->xfer_cmd.cmd_lo |= I3C_COMMAND_QUEUE_PORT_DBP;
}
}
/* Add Port read macro if reception is required */
if(xfer->rx_len)
{
xfer->xfer_cmd.cmd_lo |= I3C_COMMAND_QUEUE_PORT_READ_TRANSFER;
}
/* Add commands and data to i3c Queues */
i3c_enqueue_xfer(i3c, xfer);
}
}
/**
\fn void i3c_error_handler(I3C_Type *i3c,
i3c_xfer_t *xfer,
const uint32_t status,
const uint32_t resp)
\brief handles I3C error
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\param[in] status : interrupt status
\param[in] resp : Rx Response
\return none
*/
static void i3c_error_handler(I3C_Type *i3c, i3c_xfer_t *xfer,
const uint32_t status, const uint32_t resp)
{
uint32_t tid = 0U;
xfer->status = I3C_XFER_STATUS_ERROR;
if(status & I3C_INTR_STATUS_TRANSFER_ABORT_STS)
{
/* Message abort happened */
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_TRANSFER_ABORT_STS;
xfer->status |= I3C_XFER_STATUS_ERROR_XFER_ABORT;
}
else
{
tid = I3C_RESPONSE_QUEUE_PORT_TID(resp);
switch(tid)
{
case I3C_MST_TX_TID:
case I3C_SLV_TX_TID:
case I3C_CCC_SET_TID:
xfer->status |= I3C_XFER_STATUS_ERROR_TX;
break;
case I3C_MST_RX_TID:
case I3C_SLV_RX_TID:
case I3C_CCC_GET_TID:
xfer->status |= I3C_XFER_STATUS_ERROR_RX;
break;
case I3C_ADDR_ASSIGN_TID:
xfer->tx_len = I3C_RESPONSE_QUEUE_PORT_DATA_LEN(resp);
xfer->status |= I3C_XFER_STATUS_ERROR_ADDR_ASSIGN;
break;
}
}
}
/**
\fn static void i3c_ibi_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief i3c interrupt service routine for In-band-interrupts
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
static void i3c_ibi_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t ibi_resp = 0U;
/* Checks for the IBI status as a master */
ibi_resp = i3c->I3C_IBI_QUEUE_STATUS;
if(ibi_resp)
{
ibi_resp = I3C_IBI_QUEUE_STATUS_IBI_ID(ibi_resp);
if(ibi_resp == I3C_HOT_JOIN_ID)
{
/* Hot-Join is requested by new slave */
xfer->status = (I3C_XFER_STATUS_SLV_HOT_JOIN_REQ |
I3C_XFER_STATUS_DONE);
}
/* Checks if it is a IBI slave interrupt request */
else if(I3C_IBI_QUEUE_STATUS_IBI_ID_RW(ibi_resp))
{
/* IBI slave interrupt requested by new slave */
xfer->addr = (ibi_resp >> I3C_IBI_QUEUE_STATUS_IBI_ID_RW_Pos);
xfer->status = (I3C_XFER_STATUS_IBI_SLV_INTR_REQ |
I3C_XFER_STATUS_DONE);
}
/* Checks if it is a Mastership request */
else if(!(I3C_IBI_QUEUE_STATUS_IBI_ID_RW(ibi_resp)))
{
xfer->addr = (ibi_resp >> I3C_IBI_QUEUE_STATUS_IBI_ID_RW_Pos);
/* Mastership is requested by new slave */
xfer->status = (I3C_XFER_STATUS_IBI_MASTERSHIP_REQ |
I3C_XFER_STATUS_DONE);
}
}
else
{
/* Checks the IBI status as a slave */
ibi_resp = ((i3c->I3C_SLV_INTR_REQ & I3C_SLV_INTR_REQ_IBI_STS_Msk) >>
I3C_SLV_INTR_REQ_IBI_STS_Pos);
/* Checks if the master has acked the IBI request */
if(ibi_resp == I3C_SLV_INTR_REQ_IBI_STS_MST_ACKED)
{
xfer->status = I3C_XFER_STATUS_DONE;
}
/* Checks if IBI is not initiated only */
else if(ibi_resp == I3C_SLV_INTR_REQ_IBI_STS_NATMPTED)
{
xfer->status = I3C_XFER_STATUS_ERROR;
}
}
}
/**
\fn int32_t i3c_slave_req_bus_mastership(I3C_Type *i3c)
\brief Sends mastership request to master
\param[in] i3c : Pointer to i3c register set structure
\return exec status
*/
int32_t i3c_slave_req_bus_mastership(I3C_Type *i3c)
{
/* Returns -1 if the device dynamic address is invalid */
if(!(i3c->I3C_DEVICE_ADDR & I3C_DEVICE_ADDR_DYNAMIC_ADDR_VALID))
{
return -1;
}
/* Returns -1 if the device doesn't have Secondary Master capability */
if((i3c->I3C_HW_CAPABILITY & I3C_HW_CAPABILITY_DEVICE_ROLE_CONFIG_Msk) !=
I3C_DEVICE_ROLE_SECONDARY_MASTER)
{
return -1;
}
/* Returns -1 if Master Request option is disabled by master */
if(!(i3c->I3C_SLV_EVENT_STATUS & I3C_SLV_EVENT_STATUS_MR_EN))
{
return -1;
}
/* Returns -1 if either MR or SIR is already requested */
if(i3c->I3C_SLV_INTR_REQ & (I3C_SLV_INTR_REQ_MR | I3C_SLV_INTR_REQ_SIR))
{
return -1;
}
/* Accept master acceptance command */
i3c->I3C_DEVICE_CTRL_EXTENDED &= ~I3C_DEVICE_CTRL_EXTENDED_REQMST_ACK_CTRL;
/* Enable updated ownership interrupt */
i3c->I3C_INTR_STATUS_EN |= (I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN |
I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN);
i3c->I3C_INTR_SIGNAL_EN |= (I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN |
I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN);
/* Enable Master request */
i3c->I3C_SLV_INTR_REQ |= I3C_SLV_INTR_REQ_MR;
return 0;
}
/**
\fn int32_t i3c_slave_tx_slv_intr_req(I3C_Type *i3c)
\brief Sends Slave Interrupt request to master
\param[in] i3c : Pointer to i3c register set structure
\return exec status
*/
int32_t i3c_slave_tx_slv_intr_req(I3C_Type *i3c)
{
/* Returns -1 if the device dynamic address is invalid */
if(!(i3c->I3C_DEVICE_ADDR & I3C_DEVICE_ADDR_DYNAMIC_ADDR_VALID))
{
return -1;
}
/* Returns -1 if Slave Interrupt Request option is disabled by master */
if(!(i3c->I3C_SLV_EVENT_STATUS & I3C_SLV_EVENT_STATUS_SIR_EN))
{
return -1;
}
/* Returns -1 if either MR or SIR is already requested */
if(i3c->I3C_SLV_INTR_REQ & (I3C_SLV_INTR_REQ_MR | I3C_SLV_INTR_REQ_SIR))
{
return -1;
}
/* Enable updated ownership interrupt */
i3c->I3C_INTR_STATUS_EN |= I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN;
i3c->I3C_INTR_SIGNAL_EN |= I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN;
/* Enable Slave Interrupt request */
i3c->I3C_SLV_INTR_REQ &= I3C_SLV_INTR_REQ_SIR_CTRL;
i3c->I3C_SLV_INTR_REQ |= I3C_SLV_INTR_REQ_SIR;
return 0;
}
/**
\fn uint8_t i3c_get_slv_dyn_addr(I3C_Type *i3c,
const uint8_t static_addr)
\brief Gets slave's dynamic from DAT for the
given static address
\param[in] i3c : Pointer to i3c register set structure
\param[in] static_addr : Slave's static address
\return none
*/
uint8_t i3c_get_slv_dyn_addr(I3C_Type *i3c,
const uint8_t static_addr)
{
uint8_t iter = 0U;
uint32_t datp = i3c_get_dat_addr(i3c);
uint32_t dat_addr = 0U;
uint32_t dest_addr = 0U;
for(iter = 0U; iter < I3C_MAX_DEVS; iter++)
{
/* DAT address = i3c Base + DAT Base + (Pos * 4) */
dat_addr = (uint32_t)i3c + datp + (iter << 2);
dest_addr = *((volatile uint32_t *) (dat_addr));
if((dest_addr & (I3C_DAT_STATIC_ADDR_Msk)) == static_addr)
{
return ((dest_addr & I3C_DAT_DYNAMIC_ADDR_Msk) >>
I3C_DAT_DYNAMIC_ADDR_Pos);
}
}
return 0U;
}
/**
\fn void i3c_sec_master_get_dct(I3C_Type *i3c,
\ i3c_dev_char_t *data,
\ const uint8_t slv_cnt)
\brief Get Device Characteritics Table of slaves.
\param[in] i3c : Pointer to i3c register set structure
\param[in] data : Device characteristics data
\param[in] slv_cnt : Slaves count
\return None
*/
void i3c_sec_master_get_dct(I3C_Type *i3c,
i3c_dev_char_t *data,
const uint8_t slv_cnt)
{
uint8_t iter;
uint32_t dctp = i3c_get_dct_addr(i3c);
volatile uint32_t *dct_ptr;
/* DCT address = i3c Base + DCT Offset */
dct_ptr = (volatile uint32_t *)((uint32_t)i3c + dctp);
/* Fetches slaves information received through
* DEFSLVS CCC */
for(iter = 0U; iter < slv_cnt; iter++)
{
data->static_addr = I3C_SEC_DEV_CHAR_TABLE1_STATIC_ADDR(*dct_ptr);
data->bcr = I3C_SEC_DEV_CHAR_TABLE1_BCR_TYPE(*dct_ptr);
data->dcr = I3C_SEC_DEV_CHAR_TABLE1_DCR_TYPE(*dct_ptr);
data->dynamic_addr = I3C_SEC_DEV_CHAR_TABLE1_DYNAMIC_ADDR(*dct_ptr);
data++;
dct_ptr++;
}
}
/**
\fn void i3c_master_get_dct(I3C_Type *i3c,
\ i3c_dev_prime_info_t *data,
\ const uint8_t addr)
\brief Get Device Characteritics Table of requested slave.
\param[in] i3c : Pointer to i3c register set structure
\param[in] data : Device characteristics data
\param[in] addr : Slave's dynamic address
\return None
*/
void i3c_master_get_dct(I3C_Type *i3c,
i3c_dev_prime_info_t *data,
const uint8_t addr)
{
uint8_t iter;
volatile uint32_t *dct_ptr;
volatile uint32_t *ptr;
uint32_t dctp = i3c_get_dct_addr(i3c);
uint8_t dev_cnt = i3c_get_dct_cur_idx(i3c);
/* Finds */
for(iter = 0U; iter < dev_cnt; iter++)
{
/* DCT address = i3c Base + DCT Offset + (iter * 16)*/
dct_ptr = (volatile uint32_t *)((uint32_t)i3c + dctp + (iter << 4));
ptr = (volatile uint32_t *)
((uint32_t)dct_ptr + I3C_DCT_DYNAMIC_ADDR_OFFSET);
if(((*ptr) & I3C_DCT_DYNAMIC_ADDR_Msk) == addr)
{
/* If the DCT slave address is matching with addr,
* then store its PID and device characteristics */
data->dev_char.dynamic_addr = ((*ptr) & I3C_DCT_DYNAMIC_ADDR_Msk);
data->dev_char.static_addr = 0U;
ptr--;
data->dev_char.dcr = ((*ptr) & I3C_DCT_DCR_Msk);
data->dev_char.bcr = (((*ptr) & I3C_DCT_BCR_Msk) >>
I3C_DCT_BCR_Pos);
ptr--;
data->pid.dcr = ((*ptr) & I3C_DCT_PID_DCR_Msk);
data->pid.inst_id = (((*ptr) & I3C_DCT_INST_ID_Msk) >>
I3C_DCT_INST_ID_Pos);
ptr--;
data->pid.part_id = ((*ptr) & I3C_DCT_PART_ID_Msk);
data->pid.pid_sel = (((*ptr) & I3C_DCT_ID_SEL_Msk) >>
I3C_DCT_ID_SEL_Pos);
data->pid.mipi_mfg_id = (((*ptr) & I3C_DCT_MIPI_MFG_ID_Msk) >>
I3C_DCT_MIPI_MFG_ID_Pos);
break;
}
}
}
/**
\fn void i3c_master_tx(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief send master transmit command to i3c bus.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_master_tx(I3C_Type *i3c, i3c_xfer_t *xfer)
{
xfer->xfer_cmd.cmd_id = 0U;
xfer->xfer_cmd.port_id = I3C_MST_TX_TID;
xfer->xfer_cmd.cmd_type = I3C_XFER_TYPE_DATA;
/* Dispatch commands and data to i3c Queues */
i3c_dispatch_xfer_cmd(i3c, xfer);
}
/**
\fn void i3c_master_rx(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief send master receive command to i3c bus.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_master_rx(I3C_Type *i3c, i3c_xfer_t *xfer)
{
xfer->xfer_cmd.cmd_id = 0U;
xfer->xfer_cmd.port_id = I3C_MST_RX_TID;
xfer->xfer_cmd.cmd_type = I3C_XFER_TYPE_DATA;
/* Dispatch commands to i3c Command Queue */
i3c_dispatch_xfer_cmd(i3c, xfer);
}
/**
\fn void i3c_master_tx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief perform data transfer in blocking mode.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_master_tx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t nresp = 0U;
while(!(i3c->I3C_PRESENT_STATE & I3C_PRESENT_STATE_MASTER_IDLE));
xfer->xfer_cmd.cmd_id = 0U;
xfer->xfer_cmd.port_id = I3C_MST_TX_TID;
xfer->xfer_cmd.cmd_type = I3C_XFER_TYPE_DATA;
/* Dispatch commands to i3c Command Queue */
i3c_dispatch_xfer_cmd(i3c, xfer);
/* Waits unless Tx buffer gets empty */
while((i3c->I3C_DATA_BUFFER_STATUS_LEVEL &
I3C_DATA_BUFFER_STATUS_LEVEL_TX_BUF_EMPTY_LOC_Msk) !=
I3C_MAX_DATA_BUF_LOC);
/* Waits till some response received */
while(!(i3c->I3C_QUEUE_STATUS_LEVEL &
I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk));
nresp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(nresp);
if((xfer->error) ||
(I3C_RESPONSE_QUEUE_PORT_TID(nresp) != xfer->xfer_cmd.port_id))
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* sets as Tx Error when either an error is occurred or
* the received port id mismatches with current one */
xfer->status = I3C_XFER_STATUS_ERROR_TX;
}
else
{
/* marks as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_MST_TX_DONE | I3C_XFER_STATUS_DONE);
}
}
/**
\fn void i3c_master_rx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief perform data reception in blocking mode.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_master_rx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t nresp = 0U;
while(!(i3c->I3C_PRESENT_STATE & I3C_PRESENT_STATE_MASTER_IDLE));
xfer->xfer_cmd.cmd_id = 0U;
xfer->xfer_cmd.port_id = I3C_MST_RX_TID;
xfer->xfer_cmd.cmd_type = I3C_XFER_TYPE_DATA;
/* Dispatch commands to i3c Command Queue */
i3c_dispatch_xfer_cmd(i3c, xfer);
while(1U)
{
/* Waits till some data is received */
while(!(i3c->I3C_DATA_BUFFER_STATUS_LEVEL &
I3C_DATA_BUFFER_STATUS_LEVEL_RX_BUF_BLR_Msk));
/* Waits till some response received */
while(!(i3c->I3C_QUEUE_STATUS_LEVEL &
I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk));
nresp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(nresp);
if((xfer->error) ||
(I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp) != xfer->rx_len) ||
(I3C_RESPONSE_QUEUE_PORT_TID(nresp) != xfer->xfer_cmd.port_id))
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* sets as Error when either an error is occurred or
* expected rx dat len mismatch or the
* received port id mismatches with current one */
xfer->status = I3C_XFER_STATUS_ERROR_RX;
break;
}
else
{
if (xfer->rx_buf)
{
/* Reads the received data */
i3c_read_rx_fifo(i3c, xfer->rx_buf, xfer->rx_len);
if((xfer->rx_len - I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp)) == 0U)
{
/* mark as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_MST_RX_DONE |
I3C_XFER_STATUS_DONE);
break;
}
}
else
{
break;
}
}
}
}
/**
\fn void i3c_slave_tx(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief send slave transmit command to i3c bus.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_slave_tx(I3C_Type *i3c, i3c_xfer_t *xfer)
{
/* As per mipi_i3c_databook Section 2.7.13
* no command required for transmit,
* only data length is required
*/
xfer->xfer_cmd.cmd_hi = I3C_COMMAND_QUEUE_PORT_ARG_DATA_LEN(
xfer->xfer_cmd.data_len) |
I3C_COMMAND_QUEUE_PORT_SLV_PORT_TID(I3C_SLV_TX_TID);
xfer->xfer_cmd.cmd_lo = 0U;
/* Add commands to i3c Command Queue */
i3c_enqueue_xfer(i3c, xfer);
}
/**
\fn void i3c_slave_rx(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief send slave receive command to i3c bus.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_slave_rx(I3C_Type *i3c, i3c_xfer_t *xfer)
{
/* As per mipi_i3c_user Section 7
* no command is required for slave receive
*/
xfer->xfer_cmd.cmd_hi = 0U;
xfer->xfer_cmd.cmd_lo = 0U;
/* Add commands to i3c Command Queue */
i3c_enqueue_xfer(i3c, xfer);
}
/**
\fn void i3c_slave_tx_blocking(I3C_Type *i3c,
I3C_XFER *xfer,
i3c_xfer_cmd_type *xfer_cmd)
\brief Performs slave data transmission in blocking mode.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\param[in] xfer_cmd : xfer command
\return none
*/
void i3c_slave_tx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t nresp = 0U;
/* As per mipi_i3c_databook Section 2.7.13
* no command required for transmit,
* only data length is required
*/
xfer->xfer_cmd.cmd_hi = I3C_COMMAND_QUEUE_PORT_ARG_DATA_LEN(
xfer->xfer_cmd.data_len) |
I3C_COMMAND_QUEUE_PORT_SLV_PORT_TID(I3C_SLV_TX_TID);
xfer->xfer_cmd.cmd_lo = 0U;
xfer->xfer_cmd.port_id = I3C_SLV_TX_TID;
/* Add commands to i3c Command Queue */
i3c_enqueue_xfer(i3c, xfer);
/* Waits unless Tx buffer gets empty */
while((i3c->I3C_DATA_BUFFER_STATUS_LEVEL &
I3C_DATA_BUFFER_STATUS_LEVEL_TX_BUF_EMPTY_LOC_Msk) !=
I3C_MAX_DATA_BUF_LOC);
/* Waits till some response received */
while(!(i3c->I3C_QUEUE_STATUS_LEVEL &
I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk));
nresp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(nresp);
if((xfer->error) ||
(I3C_RESPONSE_QUEUE_PORT_TID(nresp) != xfer->xfer_cmd.port_id))
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* sets as Tx Error when either an error is occurred or
* the received port id mismatches with current one */
xfer->status = I3C_XFER_STATUS_ERROR_TX;
}
else
{
/* mark as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_SLV_TX_DONE | I3C_XFER_STATUS_DONE);
}
}
/**
\fn void i3c_slave_rx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief Performs slave data reception in blocking mode.
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\param[in] xfer_cmd : xfer command
\return none
*/
void i3c_slave_rx_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t nresp = 0U;
/* As per mipi_i3c_user Section 7
* no command is required for slave receive
*/
xfer->xfer_cmd.cmd_hi = 0U;
xfer->xfer_cmd.cmd_lo = 0U;
xfer->xfer_cmd.port_id = I3C_SLV_RX_TID;
/* Add commands to i3c Command Queue */
i3c_enqueue_xfer(i3c, xfer);
while(1U)
{
/* Waits till some data is received */
while(!(i3c->I3C_DATA_BUFFER_STATUS_LEVEL &
I3C_DATA_BUFFER_STATUS_LEVEL_RX_BUF_BLR_Msk));
/* Waits till some response received */
while(!(i3c->I3C_QUEUE_STATUS_LEVEL &
I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk));
nresp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(nresp);
if((xfer->error) ||
(I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp) != xfer->rx_len) ||
(I3C_RESPONSE_QUEUE_PORT_TID(nresp) != xfer->xfer_cmd.port_id))
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* sets as Error when either an error is occurred or
* expected rx dat len mismatch or the
* received port id mismatches with current one */
xfer->status = I3C_XFER_STATUS_ERROR_RX;
}
else
{
if (xfer->rx_buf)
{
/* Reads the received data */
i3c_read_rx_fifo(i3c, xfer->rx_buf, xfer->rx_len);
if((xfer->rx_len - I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp)) == 0U)
{
/* mark as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_SLV_RX_DONE |
I3C_XFER_STATUS_DONE);
break;
}
}
else
{
break;
}
}
}
}
/**
\fn void i3c_send_xfer_cmd(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief performs master command transfer
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_send_xfer_cmd(I3C_Type *i3c, i3c_xfer_t *xfer)
{
if(xfer->xfer_cmd.cmd_type == I3C_XFER_CCC_SET)
{
xfer->xfer_cmd.port_id = I3C_CCC_SET_TID;
}
else if(xfer->xfer_cmd.cmd_type == I3C_XFER_CCC_GET)
{
xfer->xfer_cmd.port_id = I3C_CCC_GET_TID;
}
else if(xfer->xfer_cmd.cmd_type == I3C_XFER_TYPE_ADDR_ASSIGN)
{
xfer->xfer_cmd.port_id = I3C_ADDR_ASSIGN_TID;
}
/* Dispatch commands to i3c Command Queue */
i3c_dispatch_xfer_cmd(i3c, xfer);
}
/**
\fn void i3c_send_xfer_cmd_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief performs command transfer in blocking mode
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_send_xfer_cmd_blocking(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t nresp = 0U;
while(!(i3c->I3C_PRESENT_STATE & I3C_PRESENT_STATE_MASTER_IDLE));
if(xfer->xfer_cmd.cmd_type == I3C_XFER_CCC_SET)
{
xfer->xfer_cmd.port_id = I3C_CCC_SET_TID;
}
else if(xfer->xfer_cmd.cmd_type == I3C_XFER_CCC_GET)
{
xfer->xfer_cmd.port_id = I3C_CCC_GET_TID;
}
else if(xfer->xfer_cmd.cmd_type == I3C_XFER_TYPE_ADDR_ASSIGN)
{
xfer->xfer_cmd.port_id = I3C_ADDR_ASSIGN_TID;
}
/* Dispatch commands to i3c Command Queue */
i3c_dispatch_xfer_cmd(i3c, xfer);
/* Waits till some response received */
while(!(i3c->I3C_QUEUE_STATUS_LEVEL &
I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk));
nresp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(nresp);
if((xfer->error) ||
(I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp) != xfer->rx_len) ||
(I3C_RESPONSE_QUEUE_PORT_TID(nresp) != xfer->xfer_cmd.port_id))
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* sets as Error when either an error is occurred or
* expected rx dat len mismatch or the
* received port id mismatches with current one */
xfer->status = I3C_XFER_STATUS_ERROR;
xfer->rx_len = I3C_RESPONSE_QUEUE_PORT_DATA_LEN(nresp);
}
else
{
if(xfer->xfer_cmd.cmd_type == I3C_XFER_CCC_GET)
{
if (xfer->rx_buf)
{
/* Reads the received data */
i3c_read_rx_fifo(i3c, xfer->rx_buf, xfer->rx_len);
}
}
/* mark as Transfer DONE */
xfer->status = I3C_XFER_STATUS_DONE;
}
}
/**
\fn void i3c_slow_bus_clk_cfg(I3C_Type *i3c,
uint32_t core_clk)
\brief i3c slow bus clock configuration for i3c slave device
\note This function sets the clock timings as follows:
OD - 2MHz, PP - 2MHz, SDR1-SDR4 - null
\param[in] i3c : Pointer to i3c register set structure
\param[in] core_clk : core clock
\return none
*/
void i3c_slow_bus_clk_cfg(I3C_Type *i3c,
uint32_t core_clk)
{
unsigned long core_rate, core_period;
uint32_t scl_timing;
uint8_t hcnt, lcnt;
core_rate = core_clk;
/* Calculate core clk period */
core_period = DIV_ROUND_UP(REF_CLK_RATE, core_rate);
/* Calculate SCL push-pull High and Low count for
* I3C transfers targeted to I3C devices.*/
hcnt = DIV_ROUND_UP(I3C_SLOW_BUS_THIGH_NS, core_period);
if (hcnt < I3C_SCL_I3C_TIMING_CNT_MIN)
hcnt = I3C_SCL_I3C_TIMING_CNT_MIN;
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR4_SCL_RATE) - hcnt;
if (lcnt < I3C_SCL_I3C_TIMING_CNT_MIN)
lcnt = I3C_SCL_I3C_TIMING_CNT_MIN;
scl_timing = (I3C_SCL_I3C_PP_TIMING_I3C_PP_HCNT(hcnt) |
I3C_SCL_I3C_PP_TIMING_I3C_PP_LCNT(lcnt));
i3c->I3C_SCL_I3C_PP_TIMING = scl_timing;
/* set the Bus free time for initiating the transfer in master mode.*/
if (!(i3c->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_I2C_SLAVE_PRESENT))
i3c->I3C_BUS_FREE_AVAIL_TIMING = I3C_BUS_FREE_AVAIL_TIMING_BUS_FREE_TIME(lcnt);
/* SCL open-drain High and Low count (I3C) for
* I3C transfers targeted to I3C devices*/
lcnt = DIV_ROUND_UP(I3C_SLOW_BUS_TLOW_OD_NS, core_period);
scl_timing = (I3C_SCL_I3C_OD_TIMING_I3C_OD_HCNT(hcnt) |
I3C_SCL_I3C_OD_TIMING_I3C_OD_LCNT(lcnt));
i3c->I3C_SCL_I3C_OD_TIMING = scl_timing;
/* set SCL Extended Low Count Timing Register. */
i3c->I3C_SCL_EXT_LCNT_TIMING = 0U;
}
/**
\fn void i3c_normal_bus_clk_cfg(I3C_Type *i3c,
uint32_t core_clk)
\brief i3c normal bus clock configuration for i3c slave device
\note This function sets the clock timings as per MIPI I3C std
\param[in] i3c : Pointer to i3c register set structure
\param[in] core_clk : core clock
\return none
*/
void i3c_normal_bus_clk_cfg(I3C_Type *i3c,
uint32_t core_clk)
{
unsigned long core_rate, core_period;
uint32_t scl_timing;
uint8_t hcnt, lcnt;
core_rate = core_clk;
/* Calculate core clk period */
core_period = DIV_ROUND_UP(REF_CLK_RATE, core_rate);
/* Calculate SCL push-pull High and Low count for
* I3C transfers targeted to I3C devices.*/
hcnt = DIV_ROUND_UP(I3C_NORMAL_BUS_THIGH_NS, core_period) - 1;
if (hcnt < I3C_SCL_I3C_TIMING_CNT_MIN)
hcnt = I3C_SCL_I3C_TIMING_CNT_MIN;
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR0_SCL_RATE) - hcnt;
if (lcnt < I3C_SCL_I3C_TIMING_CNT_MIN)
lcnt = I3C_SCL_I3C_TIMING_CNT_MIN;
scl_timing = (I3C_SCL_I3C_PP_TIMING_I3C_PP_HCNT(hcnt) |
I3C_SCL_I3C_PP_TIMING_I3C_PP_LCNT(lcnt));
i3c->I3C_SCL_I3C_PP_TIMING = scl_timing;
/* set the Bus free time for initiating the transfer in master mode.*/
if (!(i3c->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_I2C_SLAVE_PRESENT))
i3c->I3C_BUS_FREE_AVAIL_TIMING = I3C_BUS_FREE_AVAIL_TIMING_BUS_FREE_TIME(lcnt);
/* SCL open-drain High and Low count (I3C) for
* I3C transfers targeted to I3C devices*/
lcnt = DIV_ROUND_UP(I3C_NORMAL_BUS_TLOW_OD_NS, core_period);
scl_timing = (I3C_SCL_I3C_OD_TIMING_I3C_OD_HCNT(hcnt) |
I3C_SCL_I3C_OD_TIMING_I3C_OD_LCNT(lcnt));
i3c->I3C_SCL_I3C_OD_TIMING = scl_timing;
/* set SCL Extended Low Count Timing Register. */
/* Calculate the minimum low count for SDR1 */
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
scl_timing = I3C_SCL_EXT_LCNT_TIMING_I3C_EXT_LCNT_1(lcnt);
/* Calculate the minimum low count for SDR2 */
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR2_SCL_RATE) - hcnt;
scl_timing |= I3C_SCL_EXT_LCNT_TIMING_I3C_EXT_LCNT_2(lcnt);
/* Calculate the minimum low count for SDR3 */
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR3_SCL_RATE) - hcnt;
scl_timing |= I3C_SCL_EXT_LCNT_TIMING_I3C_EXT_LCNT_3(lcnt);
/* Calculate the minimum low count for SDR4 */
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR4_SCL_RATE) - hcnt;
scl_timing |= I3C_SCL_EXT_LCNT_TIMING_I3C_EXT_LCNT_4(lcnt);
i3c->I3C_SCL_EXT_LCNT_TIMING = scl_timing;
}
/**
\fn void i2c_clk_cfg(I3C_Type *i3c,
uint32_t core_clk,
I3C_I2C_SPEED_MODE i2c_speed_mode)
\brief i3c clock configuration for legacy i2c slave device
\param[in] i3c : Pointer to i3c register set structure
\param[in] core_clk : core clock
\param[in] i2c_speed_mode : i2c Speed mode
I3C_I2C_SPEED_MODE_FMP_1_MBPS : Fast Mode Plus 1 MBPS
I3C_I2C_SPEED_MODE_FM_400_KBPS : Fast Mode 400 KBPS
I3C_I2C_SPEED_MODE_SS_100_KBPS : Standard Mode 100 KBPS
\return none
*/
void i2c_clk_cfg(I3C_Type *i3c,
uint32_t core_clk,
I3C_I2C_SPEED_MODE i2c_speed_mode)
{
unsigned long core_rate = 0U;
unsigned long core_period = 0U;
uint16_t hcnt = 0U;
uint16_t lcnt = 0U;
uint32_t scl_timing = 0U;
core_rate = core_clk;
/* Calculate core clk period */
core_period = DIV_ROUND_UP(REF_CLK_RATE, core_rate);
/* Speed Mode: Fast Mode Plus >1 MBPS (approximately 3.124 MBPS) */
if(i2c_speed_mode == I3C_I2C_SPEED_MODE_FMP_1_MBPS)
{
/* Calculate the SCL clock high period and low period count for
* I2C Fast Mode Plus transfers. */
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_RATE) - lcnt;
scl_timing = (I3C_SCL_I2C_FMP_TIMING_I2C_FMP_HCNT(hcnt) |
I3C_SCL_I2C_FMP_TIMING_I2C_FMP_LCNT(lcnt));
i3c->I3C_SCL_I2C_FMP_TIMING = scl_timing;
}
/* Speed Mode: Fast Mode 400 KBPS */
if(i2c_speed_mode == I3C_I2C_SPEED_MODE_FM_400_KBPS)
{
/* Calculate the SCL clock high period and low period count for
* I2C Fast Mode transfers. */
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
scl_timing = (I3C_SCL_I2C_FM_TIMING_I2C_FM_HCNT(hcnt) |
I3C_SCL_I2C_FM_TIMING_I2C_FM_LCNT(lcnt));
/* Set the high and low period count for FM mode */
i3c->I3C_SCL_I2C_FM_TIMING = scl_timing;
}
/* Speed Mode: Standard Mode 100 KBPS */
if(i2c_speed_mode == I3C_I2C_SPEED_MODE_SS_100_KBPS)
{
/* Calculate the SCL clock high period and low period count for
* I2C Fast Mode transfers. */
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_SS_TLOW_MIN_NS, core_period);
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_SS_SCL_RATE) - lcnt;
scl_timing =(I3C_SCL_I2C_FM_TIMING_I2C_FM_HCNT(hcnt) |
I3C_SCL_I2C_FM_TIMING_I2C_FM_LCNT(lcnt));
i3c->I3C_SCL_I2C_FM_TIMING = scl_timing;
}
/* set the Bus free time for initiating the transfer in master mode.*/
i3c->I3C_BUS_FREE_AVAIL_TIMING = I3C_BUS_FREE_AVAIL_TIMING_BUS_FREE_TIME(lcnt);
/* Set as legacy i2c device is present. */
i3c->I3C_DEVICE_CTRL |= I3C_DEVICE_CTRL_I2C_SLAVE_PRESENT;
}
/**
\fn void i3c_master_init(I3C_Type *i3c)
\brief Initialize i3c master.
This function will :
- Set mode as master
- Clear Command Queue and Data buffer Queue
- Enable i3c controller
\param[in] i3c : Pointer to i3c register
set structure
\return none
*/
void i3c_master_init(I3C_Type *i3c)
{
i3c->I3C_QUEUE_THLD_CTRL &= ~(I3C_QUEUE_THLD_CTRL_IBI_STATUS_THLD_Msk |
I3C_QUEUE_THLD_CTRL_RESP_BUF_THLD_Msk);
i3c->I3C_DATA_BUFFER_THLD_CTRL &= ~I3C_DATA_BUFFER_THLD_CTRL_RX_BUF_THLD_Msk;
/* Set operation mode as master */
i3c->I3C_DEVICE_CTRL_EXTENDED &=
~(I3C_DEVICE_CTRL_EXTENDED_DEV_OPERATION_MODE_Msk |
I3C_DEVICE_CTRL_EXTENDED_DEV_OP_MODE_SLV);
/* configure as a master */
i3c->I3C_SLV_CHAR_CTRL |= I3C_SLV_CHAR_CTRL_DEVICE_ROLE_MASTER;
/* Enable i3c controller. */
i3c->I3C_DEVICE_CTRL |= I3C_DEVICE_CTRL_ENABLE;
}
/**
\fn void i3c_slave_init(I3C_Type *i3c,
const uint8_t slv_addr,
const uint32_t core_clk)
\brief Initialize i3c slave.
This function will :
- set slave static address
- set secondary master as slave mode
- Enable i3c controller
\param[in] i3c : Pointer to i3c register
set structure
\param[in] slv_addr : Slave own Address
\param[in] core_clk : core clock
\return none
*/
void i3c_slave_init(I3C_Type *i3c,
const uint8_t slv_addr,
const uint32_t core_clk)
{
int32_t val;
uint32_t clk_period;
uint32_t timing;
/* Calculate core clk period */
clk_period = DIV_ROUND_UP(REF_CLK_RATE, core_clk);
/* As per mipi_i3c_user Section 5 */
/* DEVICE_ADDR to set static addr with its valid bit */
i3c->I3C_DEVICE_ADDR |= (I3C_DEVICE_ADDR_STATIC_ADDR_VALID | slv_addr);
/* Response buffer threshold */
i3c->I3C_QUEUE_THLD_CTRL &= ~(I3C_QUEUE_THLD_CTRL_IBI_STATUS_THLD_Msk |
I3C_QUEUE_THLD_CTRL_RESP_BUF_THLD_Msk);
i3c->I3C_DATA_BUFFER_THLD_CTRL &= ~I3C_DATA_BUFFER_THLD_CTRL_RX_BUF_THLD_Msk;
/* Slave bus control DEVICE_CTRL_EXTENDED */
val = i3c->I3C_DEVICE_CTRL_EXTENDED;
val |= I3C_DEVICE_CTRL_EXTENDED_REQMST_ACK_CTRL;
val &= ~I3C_DEVICE_CTRL_EXTENDED_DEV_OPERATION_MODE_Msk;
val |= I3C_DEVICE_CTRL_EXTENDED_DEV_OP_MODE_SLV;
i3c->I3C_DEVICE_CTRL_EXTENDED = val;
/* configure as a slave */
i3c->I3C_SLV_CHAR_CTRL &= ~I3C_SLV_CHAR_CTRL_DEVICE_ROLE_MASTER;
timing = DIV_ROUND_UP(I3C_BUS_AVAILABLE_TIME_NS, clk_period);
/* Sets bus available time to 1us as per MIPI spec */
i3c->I3C_BUS_FREE_AVAIL_TIMING = I3C_BUS_FREE_AVAIL_TIMING_BUS_AVAILABLE_TIME(
timing);
timing = DIV_ROUND_UP(I3C_BUS_IDLE_TIME_NS, clk_period);
i3c->I3C_BUS_IDLE_TIMING = I3C_BUS_IDLE_TIMING_BUS_IDLE_TIME(timing);
/* Enable i3c controller. */
i3c->I3C_DEVICE_CTRL |= I3C_DEVICE_CTRL_ENABLE;
}
/**
\fn void i3c_master_irq_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief i3c interrupt service routine for master
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_master_irq_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t status = 0U;
uint32_t nresp = 0U;
uint32_t resp = 0U;
uint32_t tid = 0U;
status = i3c->I3C_INTR_STATUS;
if (!(status & i3c->I3C_INTR_STATUS_EN))
{
/* there are no interrupts that we are interested in */
i3c->I3C_INTR_STATUS = I3C_INTR_STATUS_ALL;
return;
}
/* we are only interested in a response interrupt,
* make sure we have a response */
nresp = i3c->I3C_QUEUE_STATUS_LEVEL;
if(I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR(nresp))
{
resp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->rx_len = I3C_RESPONSE_QUEUE_PORT_DATA_LEN(resp);
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(resp);
tid = I3C_RESPONSE_QUEUE_PORT_TID(resp);
}
else
{
/* Checks for the availability of IBI */
if((I3C_QUEUE_STATUS_LEVEL_IBI_BUF_BLR(nresp)) &&
(status & I3C_INTR_STATUS_IBI_THLD_STS))
{
i3c_ibi_handler(i3c, xfer);
}
xfer->error = 0U;
tid = 0U;
}
if(xfer->error)
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* Invokes error handler */
i3c_error_handler(i3c, xfer, status, resp);
}
else
{
switch (tid)
{
case I3C_MST_TX_TID:
case I3C_CCC_SET_TID:
if (tid == I3C_MST_TX_TID)
{
xfer->status = I3C_XFER_STATUS_MST_TX_DONE;
}
if (tid == I3C_CCC_SET_TID)
{
xfer->status = I3C_XFER_STATUS_CCC_SET_DONE;
}
/* mark all success event also as Transfer DONE */
xfer->status |= I3C_XFER_STATUS_DONE;
break;
case I3C_MST_RX_TID:
case I3C_CCC_GET_TID:
if (xfer->rx_len)
{
if (xfer->rx_buf)
{
i3c_read_rx_fifo(i3c, xfer->rx_buf, xfer->rx_len);
}
if (tid == I3C_MST_RX_TID)
{
xfer->status = I3C_XFER_STATUS_MST_RX_DONE;
}
if (tid == I3C_CCC_GET_TID)
{
/* Checks for Busowner updated status */
if(status & I3C_INTR_STATUS_BUSOWNER_UPDATED_STS)
{
/* Disable Bus Ownership update interrupt */
i3c->I3C_INTR_STATUS_EN &=
(~I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN);
i3c->I3C_INTR_SIGNAL_EN &=
(~I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN);
xfer->status = I3C_XFER_STATUS_BUSOWNER_UPDATED;
i3c->I3C_INTR_STATUS |=
I3C_INTR_STATUS_BUSOWNER_UPDATED_STS;
}
else
{
xfer->status = I3C_XFER_STATUS_CCC_GET_DONE;
}
}
/* mark all success event also as Transfer DONE */
xfer->status |= I3C_XFER_STATUS_DONE;
}
break;
case I3C_ADDR_ASSIGN_TID:
/* mark all success event also as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_ADDR_ASSIGN_DONE |
I3C_XFER_STATUS_DONE);
break;
default:
break;
}
}
}
/**
\fn void i3c_slave_irq_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
\brief i3c interrupt service routine for slave
\param[in] i3c : Pointer to i3c register set structure
\param[in] xfer : Pointer to i3c transfer structure
\return none
*/
void i3c_slave_irq_handler(I3C_Type *i3c, i3c_xfer_t *xfer)
{
uint32_t status = 0U;
uint32_t nresp = 0U;
uint32_t resp = 0U;
uint32_t tid = 0U;
status = i3c->I3C_INTR_STATUS;
/* Checking for dynamic address valid */
if(status & I3C_INTR_STATUS_DYN_ADDR_ASSGN_STS)
{
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_DYN_ADDR_ASSGN_STS;
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_SLV_DYN_ADDR_ASSGN);
}
else if(!(status & i3c->I3C_INTR_STATUS_EN))
{
/* there are no interrupts that we are interested in */
i3c->I3C_INTR_STATUS = I3C_INTR_STATUS_ALL;
}
/* Checks for IBI updated status */
else if(status & I3C_INTR_STATUS_IBI_UPDATED_STS)
{
i3c_ibi_handler(i3c, xfer);
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_IBI_UPDATED_STS;
}
/* Checks for Busowner updated status */
else if(status & I3C_INTR_STATUS_BUSOWNER_UPDATED_STS)
{
/* Disable updated ownership interrupt */
i3c->I3C_INTR_STATUS_EN &= ~(I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN |
I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN);
i3c->I3C_INTR_SIGNAL_EN &= ~(I3C_INTR_STATUS_EN_BUSOWNER_UPDATED_STS_EN |
I3C_INTR_STATUS_EN_IBI_UPDATED_STS_EN);
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_BUSOWNER_UPDATED);
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_BUSOWNER_UPDATED_STS;
}
else if(status & I3C_INTR_STATUS_CCC_UPDATED_STS)
{
if(i3c->I3C_SLV_EVENT_STATUS & I3C_SLV_EVENT_STATUS_MWL_UPDATED)
{
i3c->I3C_SLV_EVENT_STATUS |= I3C_SLV_EVENT_STATUS_MWL_UPDATED;
}
else if(i3c->I3C_SLV_EVENT_STATUS & I3C_SLV_EVENT_STATUS_MRL_UPDATED)
{
i3c->I3C_SLV_EVENT_STATUS |= I3C_SLV_EVENT_STATUS_MRL_UPDATED;
/* Resume the device as it is halted for Rd len updated reason */
i3c_resume(i3c);
}
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_CCC_UPDATED_STS;
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_SLV_CCC_UPDATED);
}
else
{
/* we are only interested in a response interrupt,
* make sure we have a response */
nresp = i3c->I3C_QUEUE_STATUS_LEVEL;
nresp = I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR(nresp);
if (!nresp)
return;
resp = i3c->I3C_RESPONSE_QUEUE_PORT;
xfer->rx_len = I3C_RESPONSE_QUEUE_PORT_DATA_LEN(resp);
xfer->error = I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(resp);
if(xfer->error)
{
/* Fetches error type */
i3c_fetch_error_type(xfer);
/* Invokes error handler */
i3c_error_handler(i3c, xfer, status, resp);
}
else
{
tid = I3C_RESPONSE_QUEUE_PORT_TID(resp);
switch (tid)
{
case I3C_SLV_TX_TID:
/* mark all success event also as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_SLV_TX_DONE);
break;
case I3C_SLV_RX_TID:
if (xfer->rx_len)
{
if (xfer->rx_buf)
{
i3c_read_rx_fifo(i3c, xfer->rx_buf, xfer->rx_len);
}
/* mark all success event also as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_SLV_RX_DONE);
}
break;
case I3C_SLV_DEFSLVS_TID:
if (xfer->rx_len)
{
/* mark all success event also as Transfer DONE */
xfer->status = (I3C_XFER_STATUS_DONE |
I3C_XFER_STATUS_DEFSLV_LIST);
i3c->I3C_INTR_STATUS |= I3C_INTR_STATUS_DEFSLV_STS;
}
break;
default:
break;
}
}
}
}
/************************ (C) COPYRIGHT ALIF SEMICONDUCTOR *****END OF FILE****/