/* 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 */ /* System Includes */ #include #include "RTE_Device.h" #include "RTE_Components.h" #include CMSIS_device_header /* Project Includes */ #include "Driver_I3C_Private.h" #include "i3c.h" #include "sys_ctrl_i3c.h" #if !(RTE_I3C) #error "I3C is not enabled in the RTE_Device.h" #endif #if (defined(RTE_Drivers_I3C) && !RTE_I3C) #error "I3C not configured in RTE_Device.h!" #endif #define ARM_I3C_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(7, 3) /* driver version */ /* Driver Version */ static const ARM_DRIVER_VERSION DriverVersion = { ARM_I3C_API_VERSION, ARM_I3C_DRV_VERSION }; /* Driver Capabilities */ static const ARM_I3C_CAPABILITIES DriverCapabilities = { 1, /* Supports legacy i2c device */ 1, /* Supports Slave I2C/I3C Adaptive mode support during boot up phase */ 1, /* Supports In-Band Interrupts */ 0, /* Doesn't support In-Band Interrupt with Payload */ 1, /* Supports Secondary Master Configuration */ 1, /* Supports HDR DDR0 mode */ 0, /* Doesn't support HDR Ternary Symbol Pure-Bus mode */ 0, /* Doesn't support HDR Ternary Symbol Legacy-Inclusive-Bus mode */ 0 /* Reserved (must be zero) */ }; #if I3C_DMA_ENABLE /** \fn int32_t I3C_DMA_Initialize(DMA_PERIPHERAL_CONFIG *dma_periph) \brief Initialize DMA for I3C \param[in] dma_periph Pointer to DMA resources \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_Initialize(DMA_PERIPHERAL_CONFIG *dma_periph) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* Initializes DMA interface */ status = dma_drv->Initialize(); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_PowerControl(ARM_POWER_STATE state, DMA_PERIPHERAL_CONFIG *dma_periph) \brief PowerControl DMA for I3C \param[in] state Power state \param[in] dma_periph Pointer to DMA resources \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_PowerControl(ARM_POWER_STATE state, DMA_PERIPHERAL_CONFIG *dma_periph) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* Initializes DMA interface */ status = dma_drv->PowerControl(state); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_Allocate(DMA_PERIPHERAL_CONFIG *dma_periph) \brief Allocate a channel for I3C \param[in] dma_periph Pointer to DMA resources \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_Allocate(DMA_PERIPHERAL_CONFIG *dma_periph) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* Allocate handle for peripheral */ status = dma_drv->Allocate(&dma_periph->dma_handle); if(status) { return ARM_DRIVER_ERROR; } /* Enable the channel in the Event Router */ if(dma_periph->evtrtr_cfg.instance == 0) { evtrtr0_enable_dma_channel(dma_periph->evtrtr_cfg.channel, dma_periph->evtrtr_cfg.group, DMA_ACK_COMPLETION_PERIPHERAL); } else { evtrtrlocal_enable_dma_channel(dma_periph->evtrtr_cfg.channel, DMA_ACK_COMPLETION_PERIPHERAL); } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_DeAllocate(DMA_PERIPHERAL_CONFIG *dma_periph) \brief De-allocate channel of I3C \param[in] dma_periph Pointer to DMA resources \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_DeAllocate(DMA_PERIPHERAL_CONFIG *dma_periph) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* De-Allocate handle */ status = dma_drv->DeAllocate(&dma_periph->dma_handle); if(status) { return ARM_DRIVER_ERROR; } /* Disable the channel in the Event Router */ if(dma_periph->evtrtr_cfg.instance == 0) { evtrtr0_disable_dma_channel(dma_periph->evtrtr_cfg.channel); evtrtr0_disable_dma_handshake(dma_periph->evtrtr_cfg.channel, dma_periph->evtrtr_cfg.group); } else { evtrtrlocal_disable_dma_channel(dma_periph->evtrtr_cfg.channel); } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_Start(DMA_PERIPHERAL_CONFIG *dma_periph, ARM_DMA_PARAMS *dma_params) \brief Start I3C DMA transfer \param[in] dma_periph Pointer to DMA resources \param[in] dma_params Pointer to DMA parameters \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_Start(DMA_PERIPHERAL_CONFIG *dma_periph, ARM_DMA_PARAMS *dma_params) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* Start transfer */ status = dma_drv->Start(&dma_periph->dma_handle, dma_params); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_Stop(DMA_PERIPHERAL_CONFIG *dma_periph) \brief Stop I3C DMA transfer \param[in] dma_periph Pointer to DMA resources \return \ref execution_status */ __STATIC_INLINE int32_t I3C_DMA_Stop(DMA_PERIPHERAL_CONFIG *dma_periph) { int32_t status; ARM_DRIVER_DMA *dma_drv = dma_periph->dma_drv; /* Stop transfer */ status = dma_drv->Stop(&dma_periph->dma_handle); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_Start_TX(I3C_RESOURCES *i3c, const void *src_addr, uint32_t len) \brief Start sending data to I3C TX FIFO using DMA \param[in] i3c : Pointer to i3c resources structure \param[in] src_addr : Pointer to source address \param[in] len : number of bytes \return execution_status */ static int32_t I3C_DMA_Start_TX(I3C_RESOURCES *i3c, const void *src_addr, uint32_t len) { int32_t status; ARM_DMA_PARAMS dma_params; /* Start the DMA engine for sending the data to I3C */ dma_params.peri_reqno = (int8_t)i3c->dma_cfg->dma_tx.dma_periph_req; dma_params.dir = ARM_DMA_MEM_TO_DEV; dma_params.cb_event = i3c->dma_cb; dma_params.src_addr = src_addr; dma_params.dst_addr = i3c_get_dma_tx_addr(i3c->regs); dma_params.num_bytes = len; /* i3c TX/RX FIFO is 4-byte(word) aligned, * if length is not 4-byte aligned(multiple of 4), * make it aligned by adding extra length. */ if(len % 4) { dma_params.num_bytes += (4 - (len % 4)); } dma_params.irq_priority = i3c->dma_irq_priority; /* i3c TX/RX FIFO is 4-byte(word) aligned */ dma_params.burst_size = BS_BYTE_4; dma_params.burst_len = i3c_get_tx_empty_buf_thld(i3c->regs); if( dma_params.burst_len > 16) { dma_params.burst_len = 16; } /* Start DMA transfer */ status = I3C_DMA_Start(&i3c->dma_cfg->dma_tx, &dma_params); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t I3C_DMA_Start_RX(I3C_RESOURCES *i3c, void *dst_addr uint32_t len) \brief Start receiving data from I3C RX FIFO using DMA \param[in] i3c : Pointer to i3c resources structure \param[in] dst_addr : Pointer to destination address \param[in] len : number of bytes \return execution_status */ static int32_t I3C_DMA_Start_RX(I3C_RESOURCES *i3c, void *dst_addr, uint32_t len) { ARM_DMA_PARAMS dma_params; int32_t status; /* Start the DMA engine for sending the data to i3c */ dma_params.peri_reqno = (int8_t)i3c->dma_cfg->dma_rx.dma_periph_req; dma_params.dir = ARM_DMA_DEV_TO_MEM; dma_params.cb_event = i3c->dma_cb; dma_params.src_addr = i3c_get_dma_rx_addr(i3c->regs); dma_params.dst_addr = dst_addr; dma_params.num_bytes = len; /* i3c TX/RX FIFO is 4-byte(word) aligned, * if length is not 4-byte aligned(multiple of 4), * make it aligned by adding extra length. */ if(len % 4) { dma_params.num_bytes += (4 - (len % 4)); } dma_params.irq_priority = i3c->dma_irq_priority; /* i3c TX/RX FIFO is 4-byte(word) aligned */ dma_params.burst_size = BS_BYTE_4; dma_params.burst_len = i3c_get_rx_buf_thld(i3c->regs); if( dma_params.burst_len > 16) { dma_params.burst_len = 16; } /* Start DMA transfer */ status = I3C_DMA_Start(&i3c->dma_cfg->dma_rx, &dma_params); if(status) { return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } #endif /* I3C_DMA_ENABLE */ /** \fn int I3cMasterGetAddrPos(I3C_RESOURCES *i3c, uint8_t addr) \brief Get already assigned slave address position from DAT(Device Address Table). For i3c, dynamic address and for i2c, static address is used for communication. \param[in] i3c : Pointer to i3c resources structure \param[in] addr : Assigned Slave Address; Dynamic Address for i3c, Static Address for i2c slave device \return Assigned slave address position from DAT(Device Address Table) index OR ARM_DRIVER_ERROR in case slave is not already assigned. */ static int I3cMasterGetAddrPos(I3C_RESOURCES *i3c, uint8_t addr) { uint32_t pos; for (pos = 0; pos < i3c->slave_dat.maxdevs; pos++) { if (addr == (i3c->slave_dat.addrs[pos] & (~I3C_TARGET_SLAVE_TYPE_I2C))) return pos; } return ARM_DRIVER_ERROR; } /** \fn int I3cMasterGetFreePos(I3C_RESOURCES *i3c) \brief Get free position from DAT(Device Address Table) \param[in] i3c : Pointer to i3c resources structure \return Free position from DAT OR ARM_DRIVER_ERROR in case DAT is Full. Maximum 8 Slave Devices are supported (\ref register DEVICE_ADDR_TABLE_POINTER) */ static int I3cMasterGetFreePos(I3C_RESOURCES *i3c) { uint32_t i; if (!(i3c->slave_dat.freepos & GENMASK(i3c->slave_dat.maxdevs - 1, 0))) return ARM_DRIVER_ERROR; for (i = 0; i < i3c->slave_dat.maxdevs; i++) { if (i3c->slave_dat.freepos & (1 << i)) return i; } return ARM_DRIVER_ERROR; } /** \fn int I3Cx_GetSlaveDynamicAddr(I3C_RESOURCES *i3c, uint8_t* addr_list, uint8_t count) \brief Fetches the slave's dynamic address from DAT \ given the static address \param[in] i3c : Pointer to i3c resources structure \param[in] static_addr : Slave's static address \param[in] dynamic_addr : Slave's dynamic address \return \ref execution_status */ static int I3Cx_GetSlaveDynamicAddr(I3C_RESOURCES *i3c, uint8_t static_addr, uint8_t *dynamic_addr) { if(!dynamic_addr) { return ARM_DRIVER_ERROR_PARAMETER; } *dynamic_addr = i3c_get_slv_dyn_addr(i3c->regs, static_addr); /* Returns error if the requested address not found */ if(!(*dynamic_addr)) { return ARM_DRIVER_ERROR_PARAMETER; } return ARM_DRIVER_OK; } /** \fn int I3Cx_GetSlvsInfo(I3C_RESOURCES *i3c, ARM_I3C_DEV_CHAR* data, const uint8_t value) \ Note: It is valid only for secondary masters, not for \ current bus master \brief Fetches the slaves info from DCT \param[in] i3c : Pointer to i3c resources structure \param[in] data : Device characteristics data \param[in] count : Slaves count \return \ref execution_status */ static int I3Cx_GetSlvsInfo(I3C_RESOURCES *i3c, void* data, const uint8_t value) { uint8_t iter; if(!data) { return ARM_DRIVER_ERROR_PARAMETER; } if(i3c->state.is_master) { /* Performs the following if it's a master */ /* Considering value as the slave address. * Returns error if the slave address is present in table */ if(I3cMasterGetAddrPos(i3c, value) < 0) { return ARM_DRIVER_ERROR_PARAMETER; } ARM_I3C_DEV_PRIME_INFO *prime_info = (ARM_I3C_DEV_PRIME_INFO*)data; i3c_dev_prime_info_t p_info; /* Gets slave characteristics */ i3c_master_get_dct(i3c->regs, &p_info, value); prime_info->dev_char.static_addr = p_info.dev_char.static_addr; prime_info->dev_char.bcr = p_info.dev_char.bcr; prime_info->dev_char.dcr = p_info.dev_char.dcr; prime_info->dev_char.dynamic_addr = p_info.dev_char.dynamic_addr; prime_info->pid.dcr = p_info.pid.dcr; prime_info->pid.inst_id = p_info.pid.inst_id; prime_info->pid.part_id = p_info.pid.part_id; prime_info->pid.pid_sel = p_info.pid.pid_sel; prime_info->pid.mipi_mfg_id = p_info.pid.mipi_mfg_id; } else { /* Performs the following if it's a master */ /* Considering value as the count of slaves. * Returns error if the slave count is more than * max supported slaves */ if(value > i3c->slave_dat.maxdevs) { return ARM_DRIVER_ERROR_PARAMETER; } ARM_I3C_DEV_CHAR *char_info = (ARM_I3C_DEV_CHAR*)data; /* Create device characteristics array of specified length */ i3c_dev_char_t dev_chr[I3C_MAX_DEVS]; /* Secondary master fetches slaves' characteristics */ i3c_sec_master_get_dct(i3c->regs, dev_chr, value); /* Store the Device characteristics info */ for(iter = 0U; iter < value; iter++) { char_info->static_addr = dev_chr[iter].static_addr; char_info->bcr = dev_chr[iter].bcr; char_info->dcr = dev_chr[iter].dcr; char_info->dynamic_addr = dev_chr[iter].dynamic_addr; char_info++; } } return ARM_DRIVER_OK; } /** \fn int I3Cx_GetSlaveList(I3C_RESOURCES *i3c, uint8_t* addr_list, uint8_t count) \brief Fetches the slaves list from address table \param[in] i3c : Pointer to i3c resources structure \param[in] addr_list : Slave address list \param[in] count : Slaves count \return \ref execution_status */ static int I3Cx_GetSlaveList(I3C_RESOURCES *i3c, uint8_t* addr_list, uint8_t* count) { uint8_t pos = 0U; if((!addr_list) || (!count)) { return ARM_DRIVER_ERROR_PARAMETER; } (*count) = 0U; /* Fetches all valid slave addresses*/ for (pos = 0; pos < i3c->slave_dat.maxdevs; pos++) { if (!(i3c->slave_dat.freepos & (1 << pos))) { *addr_list = (i3c->slave_dat.addrs[pos] & (~I3C_TARGET_SLAVE_TYPE_I2C)); addr_list++; (*count)++; } } return ARM_DRIVER_OK; } /** \fn static void I3C_DetachSlaves(I3C_RESOURCES *i3c, uint8_t slv_cnt) \brief Detach already attached i3c devices from the i3c bus. \param[in] i3c : Pointer to i3c resources structure \param[in] slv_cnt : Slaves count \param[in] ccc_cmd : CCC command \return None */ static void I3C_DetachSlaves(I3C_RESOURCES *i3c, uint8_t slv_cnt, uint32_t ccc_cmd) { int8_t pos = 0; if((ccc_cmd == I3C_CCC_ENTDAA) || (ccc_cmd == I3C_CCC_SETDASA)) { /* Perform the following when ENTDAA or SETDASA failed */ for(pos = i3c->slave_dat.maxdevs; ((pos > 0) && (slv_cnt)); pos--) { if(i3c->slave_dat.last_asgd_addr_pos & (1U << (pos - 1U))) { /* free the index */ i3c->slave_dat.freepos |= (BIT(pos - 1U)); i3c->slave_dat.addrs[(pos - 1U)] = 0U; /* clear the DAT index pos */ i3c_remove_slv_from_dat(i3c->regs, (pos - 1U)); slv_cnt--; } } } else if(ccc_cmd == I3C_CCC_RSTDAA(false)) { /* Perform the following when Direct RSTDAA is successful */ pos = I3cMasterGetAddrPos(i3c, (i3c->slave_dat.addrs[i3c->xfer.xfer_cmd.addr_index] & (~I3C_TARGET_SLAVE_TYPE_I2C))); if(pos >= 0) { i3c->slave_dat.freepos |= (BIT(pos)); i3c->slave_dat.addrs[pos] = 0U; i3c_remove_slv_from_dat(i3c->regs, pos); } } else if(ccc_cmd == I3C_CCC_RSTDAA(true)) { /* Perform the following when broadcast RSTDAA is successful */ for(pos = 0; pos < ((int8_t)i3c->slave_dat.maxdevs); pos++) { /* Detaches only I3C slaves */ if((!(i3c->slave_dat.freepos & (1U << pos))) && (!(i3c->slave_dat.addrs[pos] & I3C_TARGET_SLAVE_TYPE_I2C))) { i3c_remove_slv_from_dat(i3c->regs, pos); i3c->slave_dat.freepos |= (BIT(pos)); i3c->slave_dat.addrs[pos] = 0U; } } } else if(ccc_cmd == I3C_CCC_SETNEWDA) { /* Perform the following when Direct SETNEWDA is successful */ pos = I3cMasterGetAddrPos(i3c, (i3c->slave_dat.addrs[i3c->xfer.xfer_cmd.addr_index] & (~I3C_TARGET_SLAVE_TYPE_I2C))); if(pos >= 0) { /* Update the slave address with new one */ i3c->slave_dat.addrs[pos] = (*((uint8_t*)i3c->xfer.tx_buf) >> 1U); i3c_update_slv_addr_in_dat(i3c->regs, pos, (*((uint8_t*)i3c->xfer.tx_buf) >> 1U)); } } } /** \fn int I3C_AddDynamicAddrParity(uint8_t *dyn_addr) \brief Generates and adds the parity to dynamic address \param[in] dyn_addr : Dynamic address \return None */ static void I3C_AddDynamicAddrParity(uint8_t *dyn_addr) { uint8_t bit_iter = 0U; uint8_t xor_value = 0U; /* XOR the 1st 7 bits of dynamic address*/ xor_value = ((*dyn_addr) & (1U << 0U)); for(bit_iter = 1U; bit_iter < 7U; bit_iter++) { xor_value ^= (((*dyn_addr) >> bit_iter) & 1U); } /* Assign the negated XOR value to 7th * bit of dynamic address */ *dyn_addr |= ((~xor_value) << 7U); } /** \fn ARM_DRIVER_VERSION I3C_GetVersion(void) \brief Get i3c driver version \return i3c driver version */ static ARM_DRIVER_VERSION I3C_GetVersion(void) { return DriverVersion; } /** \fn ARM_I3C_CAPABILITIES I3C_GetCapabilities(void) \brief Get i3c driver capabilities \return i3c driver capabilities */ static ARM_I3C_CAPABILITIES I3C_GetCapabilities(void) { return DriverCapabilities; } /** \fn ARM_I3C_STATUS I3Cx_GetStatus(I3C_RESOURCES *i3c) \brief Get i3c driver status \return i3c driver status */ static ARM_I3C_STATUS I3Cx_GetStatus(I3C_RESOURCES *i3c) { /* Returns Current I3C driver status */ i3c->status.mode = i3c->state.is_master; i3c->status.ibi_slv_addr = i3c->xfer.addr; /* Fetch the last error found */ switch(i3c->xfer.error) { case I3C_COMM_ERROR_CRC: i3c->status.last_error_code = ARM_I3C_LEC_CRC_ERROR; break; case I3C_COMM_ERROR_PARITY: i3c->status.last_error_code = ARM_I3C_LEC_PARITY_ERROR; break; case I3C_COMM_ERROR_FRAME: i3c->status.last_error_code = ARM_I3C_LEC_FRAME_ERROR; break; case I3C_COMM_ERROR_IBA_NACK: i3c->status.last_error_code = ARM_I3C_LEC_IBA_NACK_ERROR; break; case I3C_COMM_ERROR_ADDR_NACK: i3c->status.last_error_code = ARM_I3C_LEC_ADDR_NACK_ERROR; break; case I3C_COMM_ERROR_BUF_UNDR_OVR_FLW: i3c->status.last_error_code = ARM_I3C_LEC_BUF_UNDER_OVERFLOW_ERROR; break; case I3C_COMM_ERROR_XFER_ABORT: i3c->status.last_error_code = ARM_I3C_LEC_TRANSFER_ABORT_ERROR; break; case I3C_COMM_ERROR_I2C_SLV_W_NACK: i3c->status.last_error_code = ARM_I3C_LEC_I2C_SLAVE_NACK_ERROR; break; case I3C_COMM_ERROR_PEC_OR_EARLY_TERM: if(i3c->state.is_master) { i3c->status.last_error_code = ARM_I3C_LEC_PEC_BYTE_ERROR; } else { i3c->status.last_error_code = ARM_I3C_LEC_EARLY_TERMINATION_ERROR; } break; default: i3c->status.last_error_code = ARM_I3C_LEC_NO_ERROR; break; } return i3c->status; } /** \fn ARM_I3C_DEVICE_INFO I3Cx_GetDeviceInfo(I3C_RESOURCES *i3c) \brief Get i3c device information \return i3c driver info */ static ARM_I3C_DEVICE_INFO I3Cx_GetDeviceInfo(I3C_RESOURCES *i3c) { ARM_I3C_DEVICE_INFO dev_info; i3c_slave_pid_t slv_pid; /* Fetch the device information from low level and return */ dev_info.prime_info.dev_char.static_addr = i3c_get_static_addr(i3c->regs); dev_info.prime_info.dev_char.dynamic_addr = i3c_get_dynamic_addr(i3c->regs); dev_info.prime_info.dev_char.dcr = i3c_get_dcr(i3c->regs); dev_info.prime_info.dev_char.bcr = i3c_get_bcr(i3c->regs); if(i3c->state.is_master) { /* Fetch master specific information */ dev_info.max_read_len = I3C_MAX_DATA_BUF_SIZE; dev_info.max_write_len = I3C_MAX_DATA_BUF_SIZE; dev_info.max_read_speed = 0U; dev_info.max_write_speed = 0U; dev_info.max_read_turnaround = 0U; memset(&dev_info.prime_info.pid, 0, sizeof(ARM_I3C_SLV_PID)); } else { /* Fetch slave specific information */ dev_info.max_read_len = i3c_slave_get_max_read_len(i3c->regs); dev_info.max_write_len = i3c_slave_get_max_write_len(i3c->regs); dev_info.max_read_speed = i3c_slave_get_max_read_speed(i3c->regs); dev_info.max_write_speed = i3c_slave_get_max_write_speed(i3c->regs); dev_info.max_read_turnaround = i3c_slave_get_max_read_turn(i3c->regs); slv_pid = i3c_slave_get_pid(i3c->regs); dev_info.prime_info.pid.dcr = slv_pid.dcr; dev_info.prime_info.pid.inst_id = slv_pid.inst_id; dev_info.prime_info.pid.part_id = slv_pid.part_id; dev_info.prime_info.pid.pid_sel = slv_pid.pid_sel; dev_info.prime_info.pid.mipi_mfg_id = slv_pid.mipi_mfg_id; } return dev_info; } /** \fn int I3Cx_MasterSendCommand(I3C_RESOURCES *i3c, ARM_I3C_CMD *ccc) \brief Send an I3C command to the slave \param[in] i3c : Pointer to i3c resources structure \param[in] ccc : Pointer to i3c command structure \return \ref execution_status */ static int I3Cx_MasterSendCommand(I3C_RESOURCES *i3c, ARM_I3C_CMD *ccc) { int32_t index; if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; if (!ccc) return ARM_DRIVER_ERROR_PARAMETER; if(ccc->len > I3C_MAX_DATA_BUF_SIZE) return ARM_DRIVER_ERROR_PARAMETER; index = I3cMasterGetAddrPos(i3c, ccc->addr); if (index < 0) { /* Returns error if it is a Direct CCC * and no such slave present.*/ if(ccc->cmd_id & I3C_CCC_DIRECT) { return ARM_DRIVER_ERROR; } } i3c->status.busy = 1; i3c->xfer.error = 0U; if (ccc->rw) /* command read */ { i3c->xfer.tx_buf = NULL; i3c->xfer.tx_len = 0U; i3c->xfer.rx_buf = ccc->data; i3c->xfer.rx_len = ccc->len; i3c->xfer.xfer_cmd.cmd_type = I3C_XFER_CCC_GET; i3c->xfer.xfer_cmd.cmd_id = ccc->cmd_id; i3c->xfer.xfer_cmd.addr_index = index; i3c->xfer.xfer_cmd.addr_depth = 1U; i3c->xfer.xfer_cmd.def_byte = ccc->def_byte; i3c->xfer.xfer_cmd.data_len = ccc->len; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke xfer cmd blocking api */ i3c_send_xfer_cmd_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; if(!(i3c->xfer.status & I3C_XFER_STATUS_DONE)) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { /* Invoke xfer cmd api */ i3c_send_xfer_cmd(i3c->regs, &(i3c->xfer)); } } /* command read */ else /* command write */ { if(ccc->cmd_id == I3C_CCC_ENTHDR(0U)) { /* Set speed to HDR-DDR speed */ i3c->xfer.xfer_cmd.speed = I3C_SPEED_HDR_DDR; i3c->status.busy = 0; } else if((ccc->cmd_id > I3C_CCC_ENTHDR(0U)) && (ccc->cmd_id <= I3C_CCC_ENTHDR(7U))) { /* Return unsupported if other HDR-DDR speeds requested */ return ARM_DRIVER_ERROR_UNSUPPORTED; } else { i3c->xfer.rx_buf = NULL; i3c->xfer.rx_len = 0U; i3c->xfer.tx_buf = ccc->data; i3c->xfer.tx_len = ccc->len; i3c->xfer.xfer_cmd.cmd_type = I3C_XFER_CCC_SET; i3c->xfer.xfer_cmd.cmd_id = ccc->cmd_id; i3c->xfer.xfer_cmd.addr_index = index; i3c->xfer.xfer_cmd.addr_depth = 1U; i3c->xfer.xfer_cmd.def_byte = ccc->def_byte; i3c->xfer.xfer_cmd.data_len = ccc->len; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke xfer cmd blocking api */ i3c_send_xfer_cmd_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; if(i3c->xfer.status != I3C_XFER_STATUS_DONE) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } else { /* If RSTDAA or SETNEWDA command is successful * then invoke detach slave fuction to remove slaves' info */ if((i3c->xfer.xfer_cmd.cmd_id == I3C_CCC_RSTDAA(true)) || (i3c->xfer.xfer_cmd.cmd_id == I3C_CCC_RSTDAA(false)) || (i3c->xfer.xfer_cmd.cmd_id == I3C_CCC_SETNEWDA)) { I3C_DetachSlaves(i3c, i3c->xfer.tx_len, i3c->xfer.xfer_cmd.cmd_id); } } } else #endif { /* Invoke xfer cmd api */ i3c_send_xfer_cmd(i3c->regs, &i3c->xfer); } } } return ARM_DRIVER_OK; } /* * Note for I3C DMA: * For proper Master and Slave communication, * There should be fix protocol between Master and Slave, * in which both should be knowing well in advanced that * how much data is going to transmit/receive from both the sides. * (currently different-different transfer data length from * Master and Slave is not supported.) */ /** \fn int I3Cx_MasterTransmit(I3C_RESOURCES *i3c, uint8_t addr, const uint8_t *data, uint16_t len) \brief Write data to the slave \param[in] i3c : Pointer to i3c resources structure \param[in] addr : Assigned Slave Address; Dynamic Address for i3c, Static Address for i2c slave device \param[in] data : Pointer to buffer with data which needs to be transmit to slave \param[in] len : Number of bytes needs to be transmit \return \ref execution_status */ static int I3Cx_MasterTransmit(I3C_RESOURCES *i3c, uint8_t addr, const uint8_t *data, uint16_t len) { int32_t index; #if I3C_DMA_ENABLE int32_t ret; #endif if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; /* Checking for Master initialization */ if ((!i3c->state.enabled) || (!i3c->state.is_master)) return ARM_DRIVER_ERROR; if (!data || !len) return ARM_DRIVER_ERROR_PARAMETER; if(len > I3C_MAX_DATA_BUF_SIZE) return ARM_DRIVER_ERROR_PARAMETER; if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; index = I3cMasterGetAddrPos(i3c, addr); if (index < 0) return ARM_DRIVER_ERROR_PARAMETER; i3c->status.busy = 1; i3c->xfer.error = 0U; i3c->xfer.rx_buf = NULL; i3c->xfer.rx_len = 0U; i3c->xfer.xfer_cmd.addr_index = index; i3c->xfer.xfer_cmd.data_len = len; #if (!I3C_DMA_ENABLE) /* update only if DMA disable */ i3c->xfer.tx_buf = data; i3c->xfer.tx_len = len; #else i3c->xfer.tx_buf = NULL; i3c->xfer.tx_len = 0U; #endif #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke master send blocking api */ i3c_master_tx_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; /* Sets the speed to SDR Maximum */ i3c->xfer.xfer_cmd.speed = I3C_SPEED_SDR0; if(!(i3c->xfer.status & I3C_XFER_STATUS_MST_TX_DONE)) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { /* Invoke master send api */ i3c_master_tx(i3c->regs, &i3c->xfer); } #if I3C_DMA_ENABLE ret = I3C_DMA_Start_TX(i3c, data, len); if(ret) { return ARM_DRIVER_ERROR; } #endif return ARM_DRIVER_OK; } /** \fn int I3Cx_MasterReceive(I3C_RESOURCES *i3c, uint8_t addr, uint8_t *data, uint16_t len) \brief Read data from the slave \param[in] i3c : Pointer to i3c resources structure \param[in] addr : Assigned Slave Address; Dynamic Address for i3c, Static Address for i2c slave device \param[in] data : Pointer to buffer for data to receive from slave \param[in] len : Number of bytes needs to be receive \return \ref execution_status */ static int I3Cx_MasterReceive(I3C_RESOURCES *i3c, uint8_t addr, uint8_t *data, uint16_t len) { int32_t index; #if I3C_DMA_ENABLE int32_t ret; #endif if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; /* Checking for Master initialization */ if ((!i3c->state.enabled) || (!i3c->state.is_master)) return ARM_DRIVER_ERROR; if (!data || !len) return ARM_DRIVER_ERROR_PARAMETER; if(len > I3C_MAX_DATA_BUF_SIZE) return ARM_DRIVER_ERROR_PARAMETER; if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; index = I3cMasterGetAddrPos(i3c, addr); if (index < 0) return ARM_DRIVER_ERROR_PARAMETER; i3c->status.busy = 1; i3c->xfer.error = 0U; i3c->xfer.tx_buf = NULL; i3c->xfer.tx_len = 0U; i3c->xfer.xfer_cmd.addr_index = index; i3c->xfer.xfer_cmd.data_len = len; i3c->xfer.rx_len = len; #if (!I3C_DMA_ENABLE) /* update only if DMA disable */ i3c->xfer.rx_buf = data; #else i3c->xfer.rx_buf = NULL; #endif #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke master receive blocking api */ i3c_master_rx_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; /* Sets the speed to SDR Maximum */ i3c->xfer.xfer_cmd.speed = I3C_SPEED_SDR0; if(!(i3c->xfer.status & I3C_XFER_STATUS_MST_RX_DONE)) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { /* Invoke master receive api */ i3c_master_rx(i3c->regs, &i3c->xfer); } #if I3C_DMA_ENABLE ret = I3C_DMA_Start_RX(i3c, data, len); if(ret) { return ARM_DRIVER_ERROR; } #endif return ARM_DRIVER_OK; } /** \fn int I3Cx_SlaveTransmit(I3C_RESOURCES *i3c, const uint8_t *data, uint16_t len) \brief Write data to the master \param[in] i3c : Pointer to i3c resources structure \param[in] data : Pointer to buffer with data which needs to be transmit to master \param[in] len : Number of bytes needs to be transmit \return \ref execution_status */ static int I3Cx_SlaveTransmit(I3C_RESOURCES *i3c, const uint8_t *data, uint16_t len) { #if I3C_DMA_ENABLE int32_t ret; #endif /* Checking for power done initialization */ if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; /* Checking for slave initialization */ if ((!i3c->state.enabled) || (i3c->state.is_master)) return ARM_DRIVER_ERROR; /* Parameter check */ if (!data || !len) return ARM_DRIVER_ERROR_PARAMETER; if((len > i3c_slave_get_max_write_len(i3c->regs)) || (len > I3C_MAX_DATA_BUF_SIZE)) { /* Return error if required legth is greater than * Master Set Max Write Length or I3C_MAX_DATA_BUF_SIZE */ return ARM_DRIVER_ERROR_PARAMETER; } if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; i3c->status.busy = 1; i3c->xfer.error = 0U; i3c->xfer.rx_buf = NULL; i3c->xfer.rx_len = 0U; i3c->xfer.xfer_cmd.data_len = len; i3c->xfer.tx_len = len; #if (!I3C_DMA_ENABLE) /* update only if DMA disable */ i3c->xfer.tx_buf = data; #else i3c->xfer.tx_buf = NULL; #endif #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke slave transmit blocking api */ i3c_slave_tx_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; if(!(i3c->xfer.status & I3C_XFER_STATUS_SLV_TX_DONE)) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { /* Invoke slave transmit api */ i3c_slave_tx(i3c->regs, &i3c->xfer); } #if I3C_DMA_ENABLE ret = I3C_DMA_Start_TX(i3c, data, len); if(ret) { return ARM_DRIVER_ERROR; } #endif return ARM_DRIVER_OK; } /** \fn int I3Cx_SlaveReceive(I3C_RESOURCES *i3c, uint8_t *data, uint32_t len) \brief Read data from the master \param[in] i3c : Pointer to i3c resources structure \param[in] data : Pointer to buffer for data to receive from master \param[in] len : Number of bytes needs to be receive \return \ref execution_status */ static int I3Cx_SlaveReceive(I3C_RESOURCES *i3c, uint8_t *data, uint32_t len) { #if I3C_DMA_ENABLE int32_t ret; #endif /* Checking for power done initialization */ if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; /* Checking for slave initialization */ if ((!i3c->state.enabled) || (i3c->state.is_master)) return ARM_DRIVER_ERROR; /* Parameter check */ if (!data || !len) return ARM_DRIVER_ERROR_PARAMETER; if((len > i3c_slave_get_max_read_len(i3c->regs)) || (len > I3C_MAX_DATA_BUF_SIZE)) { /* Return error if required legth is greater than * Master Set Max Read Length or I3C_MAX_DATA_BUF_SIZE */ return ARM_DRIVER_ERROR_PARAMETER; } if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; i3c->status.busy = 1; i3c->xfer.error = 0U; i3c->xfer.tx_buf = NULL; i3c->xfer.tx_len = 0; i3c->xfer.rx_len = len; #if (!I3C_DMA_ENABLE) /* update only if DMA disable */ /* Buffer initialization for TX/RX */ i3c->xfer.rx_buf = data; #else i3c->xfer.rx_buf = NULL; #endif #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { /* Invoke slave receive blocking api */ i3c_slave_rx_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; if(!(i3c->xfer.status & I3C_XFER_STATUS_SLV_RX_DONE)) { /* Resume the device if error occurs */ i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { /* Invoke slave receive api */ i3c_slave_rx(i3c->regs, &i3c->xfer); } #if I3C_DMA_ENABLE ret = I3C_DMA_Start_RX(i3c, data, len); if(ret) { return ARM_DRIVER_ERROR; } #endif return ARM_DRIVER_OK; } /** \fn int I3Cx_MasterAssignDA(I3C_RESOURCES *i3c, ARM_I3C_CMD *addr_cmd) \brief Assign dynamic address to the i3c slave using ENTDAA and SETDASA Note: Only required for i3c slave devices; i2c slave device uses static address for communication \ref I3Cx_AttachSlvDev. \param[in] i3c : Pointer to i3c resources structure \param[in] addr_cmd : Address CCC \return \ref execution_status */ static int I3Cx_MasterAssignDA(I3C_RESOURCES *i3c, ARM_I3C_CMD *addr_cmd) { int32_t pos = 0U; uint8_t init_pos = 0xFFU; uint8_t iter = 0U; uint8_t dyn_addr = 0U; if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; if (i3c->status.busy) return ARM_DRIVER_ERROR_BUSY; /* Perform below if it is ENTDAA CCC */ if(addr_cmd->cmd_id == I3C_CCC_ENTDAA) { if(!addr_cmd->len) { return ARM_DRIVER_ERROR_PARAMETER; } else if(addr_cmd->len > i3c->slave_dat.maxdevs) { return ARM_DRIVER_ERROR_UNSUPPORTED; } for(iter = 0U; iter < addr_cmd->len; iter++) { pos = I3cMasterGetFreePos(i3c); /* the dat is full */ if (pos < 0) { if(init_pos == 0xFFU) return ARM_DRIVER_ERROR; else break; } else { /* reserve the index */ i3c->slave_dat.freepos &= ~(BIT(pos)); i3c->slave_dat.last_asgd_addr_pos |= (BIT(pos)); /* we start assigning addresses from 0x09 */ addr_cmd->addr = (pos + I3C_NEXT_SLAVE_ADDR_OFFSET); while(true) { /* Checks if new address is not a self address and * not already assigned to some slave. If true then, * assign it otherwise increment it */ if((I3cMasterGetAddrPos(i3c, addr_cmd->addr) < 0) && (i3c_get_dynamic_addr(i3c->regs) != addr_cmd->addr)) { break; } else { addr_cmd->addr++; } } i3c->slave_dat.addrs[pos] = addr_cmd->addr; i3c->slave_dat.addrs[pos] &= (~I3C_TARGET_SLAVE_TYPE_I2C); I3C_AddDynamicAddrParity(&addr_cmd->addr); /* We have space in the dat, * program the dat in index pos */ i3c_add_slv_to_dat(i3c->regs, pos, addr_cmd->addr, 0); } /* Stores first found free address position in * init position*/ if(init_pos == 0xFFU) { init_pos = pos; } } i3c->xfer.xfer_cmd.cmd_id = addr_cmd->cmd_id; i3c->xfer.xfer_cmd.addr_index = init_pos; i3c->xfer.xfer_cmd.addr_depth = addr_cmd->len; } else { /*Returns error if slave static address is invalid */ if (!addr_cmd->addr) return ARM_DRIVER_ERROR_PARAMETER; /* Find the first unused index in freepos, note that this also * corresponds to the first unused location in the DAT */ pos = I3cMasterGetFreePos(i3c); /* the dat is full */ if (pos < 0) { return ARM_DRIVER_ERROR; } /* reserve the index */ i3c->slave_dat.freepos &= ~(BIT(pos)); i3c->slave_dat.last_asgd_addr_pos |= (BIT(pos)); /* we start assigning addresses from 0x09 */ dyn_addr = pos + I3C_NEXT_SLAVE_ADDR_OFFSET; while(true) { /* Checks if new address is not a self address and * not already assigned to some slave. If true then, * assign it otherwise increment it */ if((I3cMasterGetAddrPos(i3c, dyn_addr) < 0) && (i3c_get_dynamic_addr(i3c->regs) != dyn_addr)) { break; } else { dyn_addr++; } } i3c->slave_dat.addrs[pos] = dyn_addr; i3c->slave_dat.addrs[pos] &= (~I3C_TARGET_SLAVE_TYPE_I2C); /* We have space in the dat, * program the dat in index pos */ i3c_add_slv_to_dat(i3c->regs, pos, dyn_addr, addr_cmd->addr); i3c->xfer.xfer_cmd.cmd_id = addr_cmd->cmd_id; i3c->xfer.xfer_cmd.addr_index = pos; i3c->xfer.xfer_cmd.addr_depth = 1U; } i3c->status.busy = 1U; i3c->xfer.error = 0U; i3c->xfer.rx_len = 0U; i3c->xfer.xfer_cmd.cmd_type = I3C_XFER_TYPE_ADDR_ASSIGN; i3c->xfer.xfer_cmd.def_byte = addr_cmd->def_byte; i3c->xfer.xfer_cmd.data_len = 0U; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { i3c->xfer.rx_len = 0U; i3c_send_xfer_cmd_blocking(i3c->regs, &i3c->xfer); i3c->status.busy = 0U; if(i3c->xfer.status != I3C_XFER_STATUS_DONE) { /* Error during address assignment, * so remove slave from Device address table */ I3C_DetachSlaves(i3c, i3c->xfer.rx_len, i3c->xfer.xfer_cmd.cmd_id); i3c->slave_dat.last_asgd_addr_pos = 0U; i3c_resume(i3c->regs); return ARM_DRIVER_ERROR; } } else #endif { i3c_send_xfer_cmd(i3c->regs, &i3c->xfer); } return ARM_DRIVER_OK; } /** \fn int I3Cx_AttachSlvDev(I3C_RESOURCES *i3c, const ARM_I3C_DEVICE_TYPE dev_type, const uint8_t addr) \brief Attach legacy i2c device to the i3c bus. \param[in] i3c : Pointer to i3c resources structure \param[in] dev_type : i2c/i3c device \param[in] addr : Static/Dynamic address of slave device \return \ref execution_status */ static int I3Cx_AttachSlvDev(I3C_RESOURCES *i3c, const ARM_I3C_DEVICE_TYPE dev_type, const uint8_t addr) { int32_t pos; if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; if (!addr) return ARM_DRIVER_ERROR_PARAMETER; /* Return error if the slave is already attached to * Device address table */ if(I3cMasterGetAddrPos(i3c, addr) >= 0) { return ARM_DRIVER_ERROR_PARAMETER; } /* Return error if the slave address is same as * self address */ if(addr == i3c_get_dynamic_addr(i3c->regs)) { return ARM_DRIVER_ERROR_PARAMETER; } /* Find the first unused index in freepos, note that this also * corresponds to the first unused location in the DAT(Device Address Table) */ pos = I3cMasterGetFreePos(i3c); /* DAT(Device Address Table) is full? */ if (pos < 0) { /* error: DAT is full */ return ARM_DRIVER_ERROR; } /* reserve the index */ i3c->slave_dat.freepos &= ~(BIT(pos)); if(dev_type == ARM_I3C_DEVICE_TYPE_I2C) { /* ok, we have space in the DAT, store the static address and * mark as i2c legacy device is present. */ i3c->slave_dat.addrs[pos] = (addr | I3C_TARGET_SLAVE_TYPE_I2C); /* Program the DAT(device address table) in index pos. */ i3c_add_slv_to_dat(i3c->regs, pos, 0, addr); } else { /* ok, we have space in the DAT, store the i3c address */ i3c->slave_dat.addrs[pos] = addr; /* Program the DAT(device address table) in index pos. * Store it as a dynamic address */ i3c_add_slv_to_dat(i3c->regs, pos, addr, 0); } return ARM_DRIVER_OK; } /** \fn int I3Cx_Detachdev(I3C_RESOURCES *i3c, uint8_t addr) \brief Detach already attached i2c/i3c device from the i3c bus. \param[in] i3c : Pointer to i3c resources structure \param[in] addr : Static address of already attached i2c device OR Dynamic address of already attached i3c device \return \ref execution_status */ static int I3Cx_Detachdev(I3C_RESOURCES *i3c, uint8_t addr) { int32_t pos; if (!addr) return ARM_DRIVER_ERROR_PARAMETER; if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; /* Get already attached i2c device address index in * DAT (device address table). */ pos = I3cMasterGetAddrPos(i3c, addr); /* i2c i3c is not attached to DAT? */ if (pos < 0) { /* err: i2c slave device is not attached to DAT, * first attach i2c device \ref I3Cx_AttachI2Cdev */ return ARM_DRIVER_ERROR; } /* free the index */ i3c->slave_dat.freepos |= (BIT(pos)); i3c->slave_dat.addrs[pos] = 0; /* clear the DAT index pos */ i3c_remove_slv_from_dat(i3c->regs, pos); return ARM_DRIVER_OK; } /** \fn int32_t I3Cx_Control(I3C_RESOURCES *i3c, uint32_t control, uint32_t arg) \brief Control i3c master and slave. \param[in] i3c : Pointer to i3c resources structure \param[in] control : Operation \param[in] arg : Argument of operation \return \ref execution_status */ static int32_t I3Cx_Control(I3C_RESOURCES *i3c, uint32_t control, uint32_t arg) { I3C_I2C_SPEED_MODE i2c_speed_mode = 0; uint8_t slv_addr = 0U; int32_t pos = 0; uint8_t retry_cnt = 0U; ARM_I3C_SLV_PID *hal_pid; i3c_slave_pid_t slv_pid; if (i3c->state.powered == 0U) return ARM_DRIVER_ERROR; bool blocking_mode = false; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) blocking_mode = true; #endif switch(control) { case I3C_MASTER_INIT: if(!i3c->state.enabled) { /* Sets the dynamic address */ i3c_master_set_dynamic_addr(i3c->regs); } /* Initialise master */ i3c_master_init(i3c->regs); if(!blocking_mode) { /* Enables interrupts */ i3c_master_enable_interrupts(i3c->regs); } /* set state as master enabled. */ i3c->state.enabled = 1U; i3c->state.is_master = 1U; break; case I3C_MASTER_SET_BUS_MODE: switch(arg) { case I3C_BUS_MODE_MIXED_FAST_I2C_FMP_SPEED_1_MBPS: case I3C_BUS_MODE_MIXED_FAST_I2C_FM_SPEED_400_KBPS: case I3C_BUS_MODE_MIXED_SLOW_I2C_SS_SPEED_100_KBPS: case I3C_BUS_MODE_MIXED_LIMITED: if(arg == I3C_BUS_MODE_MIXED_FAST_I2C_FMP_SPEED_1_MBPS) i2c_speed_mode = I3C_I2C_SPEED_MODE_FMP_1_MBPS; if(arg == I3C_BUS_MODE_MIXED_FAST_I2C_FM_SPEED_400_KBPS) i2c_speed_mode = I3C_I2C_SPEED_MODE_FM_400_KBPS; if(arg == I3C_BUS_MODE_MIXED_SLOW_I2C_SS_SPEED_100_KBPS) i2c_speed_mode = I3C_I2C_SPEED_MODE_SS_100_KBPS; if(arg == I3C_BUS_MODE_MIXED_LIMITED) i2c_speed_mode = I3C_I2C_SPEED_MODE_LIMITED; if (!(i3c->core_clk)) return ARM_DRIVER_ERROR; /* i2c clock configuration for selected Speed mode. */ i2c_clk_cfg(i3c->regs, i3c->core_clk, i2c_speed_mode); /* fall through */ case I3C_BUS_SLOW_MODE: if (!(i3c->core_clk)) return ARM_DRIVER_ERROR; /* i3c clock configuration */ i3c_slow_bus_clk_cfg(i3c->regs, i3c->core_clk); break; case I3C_BUS_NORMAL_MODE: if (!(i3c->core_clk)) return ARM_DRIVER_ERROR; /* i3c clock configuration */ i3c_normal_bus_clk_cfg(i3c->regs, i3c->core_clk); break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } break; case I3C_SLAVE_SET_ADDR: /* Initialize and Enable i3c Slave */ slv_addr = arg; i3c_slave_setup_adaptive_mode(i3c->regs, i3c->adaptive_mode); i3c_slave_init(i3c->regs, slv_addr, i3c->core_clk); if(!blocking_mode) { i3c_slave_enable_interrupts(i3c->regs); } /* set state as slave enabled. */ i3c->state.enabled = 1U; i3c->state.is_master = 0U; break; case I3C_MASTER_SET_SLAVE_NACK_RETRY_COUNT: pos = I3cMasterGetAddrPos(i3c, (uint8_t)arg); if(pos < 0) { return ARM_DRIVER_ERROR_PARAMETER; } retry_cnt = ((arg & I3C_SLAVE_NACK_RETRY_COUNT_Msk) >> I3C_SLAVE_NACK_RETRY_COUNT_Pos); if(retry_cnt > I3C_SLAVE_NACK_RETRY_COUNT_MAX) { return ARM_DRIVER_ERROR_PARAMETER; } /* Invoke low level api to set slv nack retry cnt */ i3c_set_slave_nack_retry_cnt(i3c->regs, pos, retry_cnt); break; case I3C_SET_SDA_TX_HOLD_TIME: if(arg > I3C_SDA_TX_HOLD_TIME_MAX) { return ARM_DRIVER_ERROR_PARAMETER; } /* Sets sda tx hold time */ i3c_set_sda_tx_hold_time(i3c->regs, arg); break; case I3C_MASTER_ABORT_MESSAGE_TRANSFER: i3c_abort_msg_transfer(i3c->regs); break; case I3C_MASTER_SETUP_HOT_JOIN_ACCEPTANCE: /* Sets up HJ acceptability at master side */ i3c_master_setup_hot_join_ctrl(i3c->regs, arg); break; case I3C_MASTER_SETUP_MR_ACCEPTANCE: /* Sets Master Request acceptability at master side */ i3c_master_setup_mst_req_ctrl(i3c->regs, arg); break; case I3C_MASTER_SETUP_SIR_ACCEPTANCE: /* Sets Slave Interrupt Request acceptability at master side */ i3c_master_setup_slv_intr_req_ctrl(i3c->regs, arg); break; case I3C_SLAVE_REQUEST_IBI_BUS_MASTERSHIP: if(i3c_slave_req_bus_mastership(i3c->regs) != 0) { return ARM_DRIVER_ERROR; } break; case I3C_SLAVE_SET_IBI_SIR: /* Sets Slave interrupt request to master */ if(i3c_slave_tx_slv_intr_req(i3c->regs) != 0) { return ARM_DRIVER_ERROR; } break; case I3C_SET_DEVICE_CHARACTERISTICS: /* Sets device's DCR */ i3c_set_dcr(i3c->regs, arg); break; case I3C_SLAVE_SET_PID: if(!arg) { return ARM_DRIVER_ERROR_PARAMETER; } /* Copy the PID and invoke set pid fun * to store it */ hal_pid = (ARM_I3C_SLV_PID*)arg; slv_pid.dcr = hal_pid->dcr; slv_pid.inst_id = hal_pid->inst_id; slv_pid.part_id = hal_pid->part_id; slv_pid.pid_sel = hal_pid->pid_sel; slv_pid.mipi_mfg_id = hal_pid->mipi_mfg_id; i3c_slave_set_pid(i3c->regs, slv_pid); break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } return ARM_DRIVER_OK; } /** \fn int32_t I3Cx_Initialize(I3C_RESOURCES *i3c, ARM_I3C_SignalEvent_t cb_event) \brief Initialize the i3c device. \param[in] i3c : Pointer to i3c resources structure \param[in] cb_event : Pointer to I3C Event \ref ARM_I3C_SignalEvent_t \return \ref execution_status */ static int32_t I3Cx_Initialize(I3C_RESOURCES *i3c, ARM_I3C_SignalEvent_t cb_event) { if (i3c->state.initialized == 1) return ARM_DRIVER_OK; bool blocking_mode = false; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { blocking_mode = true; } #endif /* If callback function is null in non-blocking mode, * then sends Error parameter */ if((!blocking_mode) && (!cb_event)) return ARM_DRIVER_ERROR_PARAMETER; /* set the user callback event. */ i3c->cb_event = cb_event; #if I3C_DMA_ENABLE i3c->dma_cfg->dma_rx.dma_handle = -1; i3c->dma_cfg->dma_tx.dma_handle = -1; /* Initialize DMA for I3C-Tx */ if(I3C_DMA_Initialize(&i3c->dma_cfg->dma_tx) != ARM_DRIVER_OK) return ARM_DRIVER_ERROR; /* Initialize DMA for I3C-Rx */ if(I3C_DMA_Initialize(&i3c->dma_cfg->dma_rx) != ARM_DRIVER_OK) return ARM_DRIVER_ERROR; #endif i3c->core_clk = GetSystemAPBClock(); /* set the state as initialized. */ i3c->state.initialized = 1; return ARM_DRIVER_OK; } /** \fn int32_t I3Cx_Uninitialize(I3C_RESOURCES *i3c) \brief Uninitialize the i3c device \param[in] i3c : Pointer to i3c resources structure \return \ref execution_status */ static int32_t I3Cx_Uninitialize(I3C_RESOURCES *i3c) { #if I3C_DMA_ENABLE i3c->dma_cfg->dma_rx.dma_handle = -1; i3c->dma_cfg->dma_tx.dma_handle = -1; #endif i3c->state.initialized = 0; return ARM_DRIVER_OK; } /** \fn int32_t I3Cx_PowerControl(I3C_RESOURCES *i3c, ARM_POWER_STATE state) \brief i3c power control \param[in] i3c : Pointer to i3c resources structure \param[in] state : Power state \return \ref execution_status */ static int32_t I3Cx_PowerControl(I3C_RESOURCES *i3c, ARM_POWER_STATE state) { bool blocking_mode = false; #if RTE_I3C_BLOCKING_MODE_ENABLE if(i3c->blocking_mode) { blocking_mode = true; } #endif switch (state) { case ARM_POWER_OFF: /* Perform below steps if not blocking mode */ if(!blocking_mode) { /* Disable i3c IRQ */ NVIC_DisableIRQ(i3c->irq); /* Clear Any Pending i3c IRQ */ NVIC_ClearPendingIRQ(i3c->irq); } #if I3C_DMA_ENABLE /* Disable i3c DMA */ i3c_dma_disable(i3c->regs); /* Deallocate DMA channel for Tx */ if(I3C_DMA_DeAllocate(&i3c->dma_cfg->dma_tx)) return ARM_DRIVER_ERROR; /* Deallocate DMA channel for Rx */ if(I3C_DMA_DeAllocate(&i3c->dma_cfg->dma_rx)) return ARM_DRIVER_ERROR; #endif /* I3C_DMA_ENABLE */ /* i3c EXPMST0 control configuration: * Disable i3c clock. */ disable_i3c_clock(); /* Reset the power state. */ i3c->state.enabled = 0U; i3c->state.is_master = 0U; i3c->state.powered = 0; break; case ARM_POWER_FULL: if (i3c->state.initialized == 0U) return ARM_DRIVER_ERROR; if (i3c->state.powered) break; /* i3c EXPMST0 control configuration: * Enable i3c clock. */ enable_i3c_clock(); #if I3C_DMA_ENABLE /* if DMA2 is selected? */ if(i3c->dma_cfg->dma_tx.evtrtr_cfg.instance == 2) { select_i3c_dma2(); } /* else: default DMA0 is selected. */ /* Enable i3c DMA */ i3c_dma_enable(i3c->regs); #endif /* I3C_DMA_ENABLE */ /* Perform below steps if not blocking mode */ if(!blocking_mode) { /* Enable i3c IRQ */ NVIC_ClearPendingIRQ(i3c->irq); NVIC_SetPriority(i3c->irq, i3c->irq_priority); NVIC_EnableIRQ(i3c->irq); } i3c->slave_dat.datp = i3c_get_dat_addr(i3c->regs); i3c->slave_dat.maxdevs = i3c_get_dat_depth(i3c->regs); i3c->slave_dat.freepos = GENMASK(i3c->slave_dat.maxdevs - 1, 0); /* Set the state as powered */ i3c->state.enabled = 0U; i3c->state.is_master = 0U; i3c->state.powered = 1; break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } #if I3C_DMA_ENABLE /* Power Control DMA for I3C-Tx */ if(I3C_DMA_PowerControl(state, &i3c->dma_cfg->dma_tx) != ARM_DRIVER_OK) { i3c->state.powered = 0; return ARM_DRIVER_ERROR; } /* Power Control DMA for I3C-Rx */ if(I3C_DMA_PowerControl(state, &i3c->dma_cfg->dma_rx) != ARM_DRIVER_OK) { i3c->state.powered = 0; return ARM_DRIVER_ERROR; } if(state == ARM_POWER_FULL) { /* Try to allocate a DMA channel for TX */ if(I3C_DMA_Allocate(&i3c->dma_cfg->dma_tx)) { i3c->state.powered = 0; return ARM_DRIVER_ERROR; } /* Try to allocate a DMA channel for RX */ if(I3C_DMA_Allocate(&i3c->dma_cfg->dma_rx)) { i3c->state.powered = 0; return ARM_DRIVER_ERROR; } } #endif /* I3C_DMA_ENABLE */ return ARM_DRIVER_OK; } #if I3C_DMA_ENABLE /** \fn static void I3Cx_DMACallback(uint32_t event, int8_t peri_num, I3C_RESOURCES *i3c) \brief Callback function from DMA for I3C \param[in] event Event from DMA \param[in] peri_num Peripheral number \param[in] I3C Pointer to I3C resources \return none */ static void I3Cx_DMACallback(uint32_t event, int8_t peri_num, I3C_RESOURCES *i3c) { if(!i3c->cb_event) return; /* Transfer Completed */ if(event & ARM_DMA_EVENT_COMPLETE) { switch(peri_num) { case I3C_DMA_TX_PERIPH_REQ: /* For DMA TX, * Success/Error decision will be taken by * Interrupt Handler based on status of Response-Queue. * (as this callback will be always called * irrespective of slave gives ACK/NACK.) */ break; case I3C_DMA_RX_PERIPH_REQ: /* For DMA RX, * Success decision will be taken here(DMA RX Callback). * Error decision will be taken by Interrupt Handler * based on status of Response-Queue. */ /* clear transfer status. */ i3c->xfer.status = I3C_XFER_STATUS_NONE; /* clear busy flag. */ i3c->status.busy = 0; /* Mark event as success and call the user callback */ i3c->cb_event(ARM_I3C_EVENT_TRANSFER_DONE); break; default: break; } } /* Abort Occurred */ if(event & ARM_DMA_EVENT_ABORT) { } } #endif /* RTE_I3C_DMA_ENABLE */ /* I3C Driver Instance */ #if (RTE_I3C) #if RTE_I3C_DMA_ENABLE static void I3C_DMACallback(uint32_t event, int8_t peri_num); static I3C_DMA_HW_CONFIG I3Cx_DMA_HW_CONFIG = { .dma_rx = { .dma_drv = &ARM_Driver_DMA_(I3C_DMA), .dma_periph_req = I3C_DMA_RX_PERIPH_REQ, .evtrtr_cfg = { .instance = I3C_DMA, .group = I3C_DMA_GROUP, .channel = I3C_DMA_RX_PERIPH_REQ, .enable_handshake = I3C_DMA_HANDSHAKE_ENABLE, }, }, .dma_tx = { .dma_drv = &ARM_Driver_DMA_(I3C_DMA), .dma_periph_req = I3C_DMA_TX_PERIPH_REQ, .evtrtr_cfg = { .instance = I3C_DMA, .group = I3C_DMA_GROUP, .channel = I3C_DMA_TX_PERIPH_REQ, .enable_handshake = I3C_DMA_HANDSHAKE_ENABLE, }, }, }; #endif /* RTE_I3C_DMA_ENABLE */ /* I3C Device Resources */ static I3C_RESOURCES i3c = { .regs = (I3C_Type *)I3C_BASE, .cb_event = (void*)0, .xfer = { .xfer_cmd = {0}, .tx_len = 0, .rx_len = 0, .tx_buf = (void*)0, .rx_buf = (void*)0, .status = I3C_XFER_STATUS_NONE, .error = 0, .addr = 0 }, .status = {0}, .state = {0}, #if RTE_I3C_BLOCKING_MODE_ENABLE .blocking_mode = true, #endif .adaptive_mode = RTE_I3C_SLAVE_ADAPTIVE_MODE_ENABLE, .irq = (IRQn_Type) I3C_IRQ_IRQn, .irq_priority = RTE_I3C_IRQ_PRI, #if RTE_I3C_DMA_ENABLE .dma_cb = I3C_DMACallback, .dma_cfg = &I3Cx_DMA_HW_CONFIG, .dma_irq_priority = RTE_I3C_DMA_IRQ_PRI, #endif }; #if RTE_I3C_DMA_ENABLE /** \fn static void I3C_DMACallback (uint32_t event, int8_t peri_num) \param[in] event Event from DMA \param[in] peri_num Peripheral number \brief Callback function from DMA for I3C */ static void I3C_DMACallback(uint32_t event, int8_t peri_num) { I3Cx_DMACallback(event, peri_num, &i3c); } #endif /* RTE_I3C_DMA_ENABLE */ void I3C_IRQHandler(void) { i3c_xfer_t *xfer = &(i3c.xfer); uint32_t event = 0; /* Invokes master ISR if the mode is master, else invokes slave ISR */ if(i3c.state.is_master) { i3c_master_irq_handler(i3c.regs, xfer); } else { i3c_slave_irq_handler(i3c.regs, xfer); } /* check status: Transfer Error? */ if(xfer->status & I3C_XFER_STATUS_ERROR) { #if RTE_I3C_DMA_ENABLE /* Stop DMA TX transfer */ if(xfer->status & I3C_XFER_STATUS_ERROR_TX) { I3C_DMA_Stop(&i3c.dma_cfg->dma_tx); } /* Stop DMA RX transfer */ if(xfer->status & I3C_XFER_STATUS_ERROR_RX) { I3C_DMA_Stop(&i3c.dma_cfg->dma_rx); } #endif /* RTE_I3C_DMA_ENABLE */ /* mark event as Transfer Error. */ event = ARM_I3C_EVENT_TRANSFER_ERROR; if(xfer->status & I3C_XFER_STATUS_ERROR_ADDR_ASSIGN) { /* Error during address assignment, * so remove slave from Device address table */ I3C_DetachSlaves(&i3c, i3c.xfer.tx_len, i3c.xfer.xfer_cmd.cmd_id); i3c.slave_dat.last_asgd_addr_pos = 0U; } else if(xfer->status & I3C_XFER_STATUS_ERROR_XFER_ABORT) { /* mark event as Transfer Error. */ event = ARM_I3C_EVENT_MESSAGE_TRANSFER_ABORT; } /* error: Resume i3c controller and * clear error status. */ i3c_resume(i3c.regs); i3c_clear_xfer_error(i3c.regs); } /* if I3C_XFER_STATUS_ERROR */ /* check status: Transfer Success? */ else if(xfer->status & I3C_XFER_STATUS_DONE) { /* mark event as Transfer done. */ event = ARM_I3C_EVENT_TRANSFER_DONE; if(xfer->status & I3C_XFER_STATUS_SLV_HOT_JOIN_REQ) { /* mark event as Hot Join request */ event = ARM_I3C_EVENT_IBI_HOT_JOIN_REQ; } else if(xfer->status & I3C_XFER_STATUS_IBI_SLV_INTR_REQ) { /* mark event as SIR request */ event = ARM_I3C_EVENT_IBI_SLV_INTR_REQ; } else if(xfer->status & I3C_XFER_STATUS_DEFSLV_LIST) { i3c.status.defslv_cnt = xfer->rx_len; /* mark event as slave list received */ event = ARM_I3C_EVENT_SLAVE_LIST; } else if(xfer->status & I3C_XFER_STATUS_IBI_MASTERSHIP_REQ) { if(I3cMasterGetAddrPos(&i3c, xfer->addr) != ARM_DRIVER_ERROR) { /* mark event as Mastership request */ event = ARM_I3C_EVENT_IBI_MASTERSHIP_REQ; } else { /* clear transfer status. */ xfer->status = I3C_XFER_STATUS_NONE; /* clear busy flag. */ i3c.status.busy = 0; /* Sets the speed to SDR Maximum */ i3c.xfer.xfer_cmd.speed = I3C_SPEED_SDR0; return; } } else if(xfer->status & I3C_XFER_STATUS_BUSOWNER_UPDATED) { if(i3c_is_master(i3c.regs)) { i3c.state.is_master = 1U; } else { i3c.state.is_master = 0U; memset(&i3c.slave_dat, 0, sizeof(I3C_SLAVE_DAT_TYPE)); } /* Flushes all buffers and resumes */ i3c_flush_all_buffers(i3c.regs); i3c_resume(i3c.regs); /* mark event as Bus owner updated */ event = ARM_I3C_EVENT_BUSOWNER_UPDATED; } else if(xfer->status & I3C_XFER_STATUS_ADDR_ASSIGN_DONE) { i3c.slave_dat.last_asgd_addr_pos = 0U; } else if(xfer->status & I3C_XFER_STATUS_CCC_SET_DONE) { if((i3c.xfer.xfer_cmd.cmd_id == I3C_CCC_RSTDAA(true)) || (i3c.xfer.xfer_cmd.cmd_id == I3C_CCC_RSTDAA(false)) || (i3c.xfer.xfer_cmd.cmd_id == I3C_CCC_SETNEWDA)) { I3C_DetachSlaves(&i3c, i3c.xfer.tx_len, i3c.xfer.xfer_cmd.cmd_id); } } else if(xfer->status & I3C_XFER_STATUS_SLV_CCC_UPDATED) { /* mark event as Slave CCC updated */ event = ARM_I3C_EVENT_SLAVE_CCC_UPDATED; } /* check status: Slave dynamic address assign? (only for Slave mode) */ else if(xfer->status & I3C_XFER_STATUS_SLV_DYN_ADDR_ASSGN) { /* mark event as Slave dynamic address assignment done. */ event = ARM_I3C_EVENT_SLV_DYN_ADDR_ASSGN; } #if RTE_I3C_DMA_ENABLE /* DMA TX, * Success/Error decision will be taken by * Interrupt Handler based on status of Response-Queue. * * DMA RX, * Success decision will be taken in DMA_RX callback. * Error decision will be taken by Interrupt Handler * based on status of Response-Queue. */ if( (xfer->status & I3C_XFER_STATUS_MST_RX_DONE) || (xfer->status & I3C_XFER_STATUS_SLV_RX_DONE) ) { /* for DMA RX Success, mark event as 0. */ event = 0; /* clear transfer status. */ xfer->status = I3C_XFER_STATUS_NONE; } #endif /* RTE_I3C_DMA_ENABLE */ }/* else if I3C_XFER_STATUS_DONE*/ if(event) { /* clear transfer status. */ xfer->status = I3C_XFER_STATUS_NONE; /* clear busy flag. */ i3c.status.busy = 0; /* call the user callback */ if(i3c.cb_event) i3c.cb_event(event); } /* Sets the speed to SDR Maximum */ i3c.xfer.xfer_cmd.speed = I3C_SPEED_SDR0; } /* wrapper functions for I3C */ static ARM_I3C_STATUS I3C_GetStatus(void) { return I3Cx_GetStatus(&i3c); } static ARM_I3C_DEVICE_INFO I3C_GetDeviceInfo(void) { return I3Cx_GetDeviceInfo(&i3c); } static int32_t I3C_Initialize(ARM_I3C_SignalEvent_t cb_event) { return (I3Cx_Initialize(&i3c, cb_event)); } static int32_t I3C_Uninitialize(void) { return (I3Cx_Uninitialize(&i3c)); } static int32_t I3C_PowerControl(ARM_POWER_STATE state) { return (I3Cx_PowerControl(&i3c, state)); } static int32_t I3C_MasterTransmit(uint8_t addr, const uint8_t *data, uint16_t len) { return (I3Cx_MasterTransmit(&i3c, addr, data, len)); } static int32_t I3C_MasterReceive(uint8_t addr, uint8_t *data, uint16_t len) { return (I3Cx_MasterReceive(&i3c, addr, data, len)); } static int32_t I3C_SlaveTransmit(const uint8_t *data, uint16_t len) { return (I3Cx_SlaveTransmit(&i3c, data, len)); } static int32_t I3C_SlaveReceive(uint8_t *data, uint16_t len) { return (I3Cx_SlaveReceive(&i3c, data, len)); } static int32_t I3C_MasterSendCommand(ARM_I3C_CMD *ccc) { return (I3Cx_MasterSendCommand(&i3c, ccc)); } static int32_t I3C_Control(uint32_t control, uint32_t arg) { return (I3Cx_Control(&i3c, control, arg)); } static int32_t I3C_MasterAssignDA(ARM_I3C_CMD *addr_cmd) { return (I3Cx_MasterAssignDA(&i3c, addr_cmd)); } static int32_t I3C_AttachSlvdev(const ARM_I3C_DEVICE_TYPE dev_type, const uint8_t addr) { return (I3Cx_AttachSlvDev(&i3c, dev_type, addr)); } static int32_t I3C_Detachdev(uint8_t addr) { return (I3Cx_Detachdev(&i3c, addr)); } static int32_t I3C_GetSlaveList(uint8_t* addr_list, uint8_t* count) { return I3Cx_GetSlaveList(&i3c, addr_list, count); } static int32_t I3C_GetSlaveDynamicAddr(const uint8_t static_addr, uint8_t *dynamic_addr) { return I3Cx_GetSlaveDynamicAddr(&i3c, static_addr, dynamic_addr); } static int32_t I3C_GetSlvsInfo(void* data, const uint8_t value) { return I3Cx_GetSlvsInfo(&i3c, data, value); } /* I3C Driver Control Block */ extern ARM_DRIVER_I3C Driver_I3C; ARM_DRIVER_I3C Driver_I3C = { I3C_GetVersion, I3C_GetCapabilities, I3C_GetStatus, I3C_GetDeviceInfo, I3C_Initialize, I3C_Uninitialize, I3C_PowerControl, I3C_MasterTransmit, I3C_MasterReceive, I3C_SlaveTransmit, I3C_SlaveReceive, I3C_Control, I3C_MasterSendCommand, I3C_MasterAssignDA, I3C_AttachSlvdev, I3C_Detachdev, I3C_GetSlaveList, I3C_GetSlaveDynamicAddr, I3C_GetSlvsInfo }; #endif /* RTE_I3C */ /************************ (C) COPYRIGHT ALIF SEMICONDUCTOR *****END OF FILE****/