/* 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 */ /**************************************************************************//** * @file I3C_Baremetal_masterside.c * @author Prabhakar kumar * @email prabhakar.kumar@alifsemi.com * @version V1.0.0 * @date 27-May-2023 * @brief Baremetal to verify master and slave loop back test * * Master sends n-bytes of data to Slave. * Slave sends back received data to Master. * then Master will compare send and received data * (Master will continue sending data in loop, * Master will stop if send and received data does not match). * * Hardware Setup: * Required two boards one for Master and one for Slave * (as there is only one i3c instance is available on ASIC). * * Connect SDA to SDA and SCL to SCL and GND to GND. * - SDA P7_6 -> SDA P7_6 * - SCL P7_7 -> SCL P7_7 * - GND -> GND * @bug None. * @Note None. ******************************************************************************/ /* System Includes */ #include #include "string.h" /* Project Includes */ /* I3C Driver */ #include "Driver_I3C.h" #include "system_utils.h" /* PINMUX Driver */ #include "pinconf.h" #include "Driver_GPIO.h" #include "RTE_Device.h" #include "RTE_Components.h" #if defined(RTE_Compiler_IO_STDOUT) #include "retarget_stdout.h" #endif /* RTE_Compiler_IO_STDOUT */ /* i3c Driver instance 0 */ extern ARM_DRIVER_I3C Driver_I3C; static ARM_DRIVER_I3C *I3Cdrv = &Driver_I3C; /* I3C slave target address */ #define I3C_SLV_TAR (0x48) /* transmit buffer from i3c */ uint8_t __ALIGNED(4) tx_data[4] = {0x00, 0x01, 0x02, 0x03}; /* receive buffer from i3c */ uint8_t __ALIGNED(4) rx_data[4] = {0x00}; uint32_t tx_cnt = 0; uint32_t rx_cnt = 0; /* flag for callback event. */ volatile uint32_t cb_event; void i3c_master_loopback_demo(); /* i3c callback events */ typedef enum _I3C_CB_EVENT{ I3C_CB_EVENT_SUCCESS = (1 << 0), I3C_CB_EVENT_ERROR = (1 << 1) }I3C_CB_EVENT; /** \fn int32_t hardware_init(void) \brief i3c hardware pin initialization: - PIN-MUX configuration - PIN-PAD configuration \param[in] void \return 0:success; -1:failure */ int32_t hardware_init(void) { /* for I3C_D(PORT_7 PIN_6(SDA)/PIN_7(SCL)) instance, * for I3C in I3C mode (not required for I3C in I2C mode) * GPIO voltage level(flex) has to be change to 1.8-V power supply. * * GPIO_CTRL Register field VOLT: * Select voltage level for the 1.8-V/3.3-V (flex) I/O pins * 0x0: I/O pin will be used with a 3.3-V power supply * 0x1: I/O pin will be used with a 1.8-V power supply */ /* Configure GPIO flex I/O pins to 1.8-V: * P7_6 and P7_7 pins are part of GPIO flex I/O pins, * so we can use any one of the pin to configure flex I/O. */ #define GPIO7_PORT 7 extern ARM_DRIVER_GPIO ARM_Driver_GPIO_(GPIO7_PORT); ARM_DRIVER_GPIO *gpioDrv = &ARM_Driver_GPIO_(GPIO7_PORT); int32_t ret = 0; uint32_t arg = 0; ret = gpioDrv->Initialize(PIN_6, NULL); if (ret != ARM_DRIVER_OK) { printf("ERROR: Failed to initialize GPIO \n"); return ARM_DRIVER_ERROR; } ret = gpioDrv->PowerControl(PIN_6, ARM_POWER_FULL); if (ret != ARM_DRIVER_OK) { printf("ERROR: Failed to powered full GPIO \n"); return ARM_DRIVER_ERROR; } /* select control argument as flex 1.8-V */ arg = ARM_GPIO_FLEXIO_VOLT_1V8; ret = gpioDrv->Control(PIN_6, ARM_GPIO_CONFIG_FLEXIO, &arg); if (ret != ARM_DRIVER_OK) { printf("ERROR: Failed to control GPIO Flex \n"); return ARM_DRIVER_ERROR; } /* I3C_SDA_D */ pinconf_set(PORT_7, PIN_6, PINMUX_ALTERNATE_FUNCTION_6, PADCTRL_READ_ENABLE | PADCTRL_DRIVER_DISABLED_PULL_UP | \ PADCTRL_OUTPUT_DRIVE_STRENGTH_4MA); /* I3C_SCL_D */ pinconf_set(PORT_7, PIN_7, PINMUX_ALTERNATE_FUNCTION_6, PADCTRL_READ_ENABLE | PADCTRL_DRIVER_DISABLED_PULL_UP | \ PADCTRL_OUTPUT_DRIVE_STRENGTH_4MA); return ARM_DRIVER_OK; } /** \fn void I3C_callback(UINT event) \brief i3c isr callback \param[in] event: i3c Event \return none */ static void I3C_callback(uint32_t event) { if (event & ARM_I3C_EVENT_TRANSFER_DONE) { cb_event = I3C_CB_EVENT_SUCCESS; } if (event & ARM_I3C_EVENT_TRANSFER_ERROR) { cb_event = I3C_CB_EVENT_ERROR; } } /** \fn void i3c_master_loopback_demo(void) \brief TestApp to verify i3c master mode loopback This demo does: - initialize i3c driver; - 1 byte of data transmitted from master and slave receive 1 byte and same 1 byte of data received by slave is transmitted through slave transmit and master receive 1byte. \return none */ void i3c_master_loopback_demo(void) { uint32_t ret = 0; uint32_t len = 0; int32_t cmp = 0; uint8_t slave_addr = 0x00; /* I3C CCC (Common Command Codes) */ ARM_I3C_CMD i3c_cmd; ARM_DRIVER_VERSION version; printf("\r\n \t\t >>> Master loop back demo starting up!!! <<< \r\n"); /* Get i3c driver version. */ version = I3Cdrv->GetVersion(); printf("\r\n i3c version api:0x%X driver:0x%X \r\n", version.api, version.drv); if((version.api < ARM_DRIVER_VERSION_MAJOR_MINOR(7U, 0U)) || (version.drv < ARM_DRIVER_VERSION_MAJOR_MINOR(7U, 0U))) { printf("\r\n Error: >>>Old driver<<< Please use new one \r\n"); return; } /* Initialize i3c hardware pins using PinMux Driver. */ ret = hardware_init(); if(ret != 0) { printf("\r\n Error: i3c hardware_init failed.\r\n"); return; } #if RTE_I3C_BLOCKING_MODE_ENABLE /* Initialize I3C driver */ ret = I3Cdrv->Initialize(NULL); #else /* Initialize I3C driver */ ret = I3Cdrv->Initialize(I3C_callback); #endif if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Initialize failed.\r\n"); return; } /* Power up I3C peripheral */ ret = I3Cdrv->PowerControl(ARM_POWER_FULL); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Power Up failed.\r\n"); goto error_uninitialize; } /* Initialize I3C master */ ret = I3Cdrv->Control(I3C_MASTER_INIT, 0); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: Master Init control failed.\r\n"); goto error_uninitialize; } /* i3c Speed Mode Configuration: Bus mode slow */ ret = I3Cdrv->Control(I3C_MASTER_SET_BUS_MODE, I3C_BUS_SLOW_MODE); /* Reject Hot-Join request */ ret = I3Cdrv->Control(I3C_MASTER_SETUP_HOT_JOIN_ACCEPTANCE, 0); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: Hot Join control failed.\r\n"); goto error_uninitialize; } /* Reject Master request */ ret = I3Cdrv->Control(I3C_MASTER_SETUP_MR_ACCEPTANCE, 0); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: Master Request control failed.\r\n"); goto error_uninitialize; } /* Reject Slave Interrupt request */ ret = I3Cdrv->Control(I3C_MASTER_SETUP_SIR_ACCEPTANCE, 0); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: Slave Interrupt Request control failed.\r\n"); goto error_uninitialize; } sys_busy_loop_us(1000); /* Assign Dynamic Address to i3c slave */ printf("\r\n >> i3c: Get dynamic addr for static addr:0x%X.\r\n",I3C_SLV_TAR); /* clear callback event flag. */ cb_event = 0; i3c_cmd.rw = 0U; i3c_cmd.cmd_id = I3C_CCC_SETDASA; i3c_cmd.len = 1U; /* Assign Slave's Static address */ i3c_cmd.addr = I3C_SLV_TAR; i3c_cmd.data = NULL; i3c_cmd.def_byte = 0U; ret = I3Cdrv->MasterAssignDA(&i3c_cmd); if(ret != ARM_DRIVER_OK) { #if !RTE_I3C_BLOCKING_MODE_ENABLE printf("\r\n Error: I3C MasterAssignDA failed.\r\n"); goto error_poweroff; #endif } #if !RTE_I3C_BLOCKING_MODE_ENABLE /* wait for callback event. */ while(!((cb_event == I3C_CB_EVENT_SUCCESS) || (cb_event == I3C_CB_EVENT_ERROR))); #endif if((cb_event == I3C_CB_EVENT_ERROR) || ((RTE_I3C_BLOCKING_MODE_ENABLE && (ret != ARM_DRIVER_OK)))) { printf("\r\n Error: First attempt failed. retrying \r\n"); /* Delay */ sys_busy_loop_us(1000); /* clear callback event flag. */ cb_event = 0; /* Observation: * Master needs to send "MasterAssignDA" two times, * First time slave is not giving ACK. */ /* Assign Dynamic Address to i3c slave */ ret = I3Cdrv->MasterAssignDA(&i3c_cmd); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C MasterAssignDA failed.\r\n"); goto error_poweroff; } #if !RTE_I3C_BLOCKING_MODE_ENABLE /* wait for callback event. */ while(!((cb_event == I3C_CB_EVENT_SUCCESS) || (cb_event == I3C_CB_EVENT_ERROR))); if(cb_event == I3C_CB_EVENT_ERROR) { printf("\nError: I3C MasterAssignDA failed\n"); while(1); } #endif } /* Get assigned dynamic address for the static address */ ret = I3Cdrv->GetSlaveDynAddr(I3C_SLV_TAR, &slave_addr); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Failed to get Dynamic Address.\r\n"); goto error_poweroff; } else { printf("\r\n >> i3c: Rcvd dyn_addr:0x%X for static addr:0x%X\r\n", slave_addr,I3C_SLV_TAR); } /* i3c Speed Mode Configuration: Normal I3C mode */ ret = I3Cdrv->Control(I3C_MASTER_SET_BUS_MODE, I3C_BUS_NORMAL_MODE); sys_busy_loop_us(1000); while(1) { len = 4; /* Delay */ sys_busy_loop_us(100); /* fill any random TX data. */ tx_data[0] += 1; tx_data[1] += 1; tx_data[2] += 1; tx_data[3] += 1; /* clear callback event flag. */ cb_event = 0; /* Master transmit */ ret = I3Cdrv->MasterTransmit(slave_addr, tx_data, len); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Master Transmit failed. \r\n"); goto error_poweroff; } #if !RTE_I3C_BLOCKING_MODE_ENABLE /* wait for callback event. */ while(!((cb_event == I3C_CB_EVENT_SUCCESS) || (cb_event == I3C_CB_EVENT_ERROR))); if(cb_event == I3C_CB_EVENT_ERROR) { printf("\nError: I3C Master transmit Failed\n"); while(1); } #endif tx_cnt += 1; /* Delay */ sys_busy_loop_us(1000); /* clear rx_data buffer */ rx_data[0] = 0x00; rx_data[1] = 0x00; rx_data[2] = 0x00; rx_data[3] = 0x00; /* clear callback event flag. */ cb_event = 0; /* Master receive */ ret = I3Cdrv->MasterReceive(slave_addr, rx_data, len); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Master Receive failed. \r\n"); goto error_poweroff; } #if !RTE_I3C_BLOCKING_MODE_ENABLE /* wait for callback event. */ while(!((cb_event == I3C_CB_EVENT_SUCCESS) || (cb_event == I3C_CB_EVENT_ERROR))); if(cb_event == I3C_CB_EVENT_ERROR) { printf("\nError: I3C Master Receive failed.\n"); while(1); } #endif rx_cnt += 1; /* compare tx and rx data, stop if data does not match */ cmp = memcmp(tx_data, rx_data, len); if(cmp != 0) { printf("\nError: TX and RX data mismatch.\n"); while(1); } } error_poweroff: /* Power off I3C peripheral */ ret = I3Cdrv->PowerControl(ARM_POWER_OFF); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Power OFF failed.\r\n"); } error_uninitialize: /* Un-initialize I3C driver */ ret = I3Cdrv->Uninitialize(); if(ret != ARM_DRIVER_OK) { printf("\r\n Error: I3C Uninitialize failed.\r\n"); } printf("\r\n I3C demo exiting...\r\n"); } /* Define main entry point. */ int main() { #if defined(RTE_Compiler_IO_STDOUT_User) int32_t ret; ret = stdout_init(); if(ret != ARM_DRIVER_OK) { while(1) { } } #endif /* Enter the ThreadX kernel. */ i3c_master_loopback_demo(); } /************************ (C) COPYRIGHT ALIF SEMICONDUCTOR *****END OF FILE****/