diff --git a/drivers/i3c/CMakeLists.txt b/drivers/i3c/CMakeLists.txt index 2bdcf3f985ddfc..c2538eb0f2c505 100644 --- a/drivers/i3c/CMakeLists.txt +++ b/drivers/i3c/CMakeLists.txt @@ -4,10 +4,10 @@ zephyr_library() zephyr_library_sources(i3c_common.c) -zephyr_library_sources_ifdef(CONFIG_I3C_ASPEED i3c_global_aspeed.c) +#zephyr_library_sources_ifdef(CONFIG_I3C_ASPEED i3c_global_aspeed.c) #zephyr_library_sources_ifdef(CONFIG_I3C_ASPEED i3c_aspeed.c) -add_subdirectory_ifdef(CONFIG_I3C_NPCM4XX NPCM4XX) +add_subdirectory_ifdef(CONFIG_I3C_NPCM4XX NPCM4XX) +add_subdirectory_ifdef(CONFIG_I3C_SLAVE slave) zephyr_library_sources_ifdef(CONFIG_I3C_NPCM4XX i3c_npcm4xx.c) -#zephyr_library_sources_ifdef(CONFIG_I3C_SHELL i3c_shell.c) -#add_subdirectory_ifdef(CONFIG_I3C_SLAVE slave) +zephyr_library_sources_ifdef(CONFIG_I3C_SHELL i3c_shell.c) \ No newline at end of file diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig index b3f67b77c51a58..c548570660fb67 100644 --- a/drivers/i3c/Kconfig +++ b/drivers/i3c/Kconfig @@ -19,7 +19,7 @@ module = I3C module-str = i3c source "subsys/logging/Kconfig.template.log_config" -#source "drivers/i3c/slave/Kconfig" +source "drivers/i3c/slave/Kconfig" # Include these first so that any properties (e.g. defaults) below can be # overridden (by defining symbols in multiple locations) diff --git a/drivers/i3c/NPCM4XX/api_i3c.c b/drivers/i3c/NPCM4XX/api_i3c.c index 5906866caa250a..f797ec55492828 100644 --- a/drivers/i3c/NPCM4XX/api_i3c.c +++ b/drivers/i3c/NPCM4XX/api_i3c.c @@ -195,6 +195,11 @@ void api_I3C_Master_Stop(__u32 Parm) I3C_Master_Stop_Request(Parm); } +void api_I3C_Master_Retry(__u32 Parm) +{ + I3C_Master_Retry_Frame(Parm); +} + void api_I3C_Master_Run_Next_Frame(__u32 Parm) { I3C_Master_Run_Next_Frame(Parm); diff --git a/drivers/i3c/NPCM4XX/i3c_core.c b/drivers/i3c/NPCM4XX/i3c_core.c index 7daac08bdc0427..e66eaf3a0bf3ba 100644 --- a/drivers/i3c/NPCM4XX/i3c_core.c +++ b/drivers/i3c/NPCM4XX/i3c_core.c @@ -318,6 +318,10 @@ I3C_ErrCode_Enum I3C_Port_Default_Setting(I3C_PORT_Enum port) pDevice->vendorID = 0; pDevice->partNumber = 0; + pDevice->max_rd_len = hal_I3C_get_MAXRD(port); + pDevice->max_wr_len = hal_I3C_get_MAXWR(port); + + pDevice->regCnt = 0; pDevice->pReg = NULL; pDevice->cmdIndex = CMD_DEFAULT; pDevice->stopSplitRead = FALSE; @@ -3064,6 +3068,7 @@ I3C_ErrCode_Enum I3C_Master_Insert_Task_CCCw(__u8 CCC, __u8 HSize, __u16 buf_siz __u16 i; __u8 fmt; __u16 dev_count; + __u8 remainder; if (HSize < 1) { return I3C_ERR_PARAMETER_INVALID; @@ -3118,6 +3123,7 @@ I3C_ErrCode_Enum I3C_Master_Insert_Task_CCCw(__u8 CCC, __u8 HSize, __u16 buf_siz } else if (CCC == CCC_DIRECT_SETMWL) { fmt = 3; } else if (CCC == CCC_DIRECT_SETMRL) { + /*fmt = 3;*/ fmt = 4; } else if ((CCC == CCC_DIRECT_SETBRGTGT) || (CCC == CCC_DIRECT_SETROUTE) || (CCC == CCC_DIRECT_SETXTIME)) { @@ -3128,7 +3134,18 @@ I3C_ErrCode_Enum I3C_Master_Insert_Task_CCCw(__u8 CCC, __u8 HSize, __u16 buf_siz return I3C_ERR_HW_NOT_SUPPORT; } - dev_count = buf_size - (HSize - 1) / fmt; +RETRY: + dev_count = (buf_size - (HSize - 1)) / fmt; + remainder = (buf_size - (HSize - 1)) % fmt; + + if (remainder != 0) { + /* try another fmt */ + if ((CCC == CCC_DIRECT_SETMRL) && (fmt == 4)) { + fmt = 3; + goto RETRY; + } + } + if (dev_count < 1) { return I3C_ERR_PARAMETER_INVALID; } diff --git a/drivers/i3c/NPCM4XX/i3c_master.c b/drivers/i3c/NPCM4XX/i3c_master.c index 51903c1b1b3d1f..e0ea9e96942e00 100644 --- a/drivers/i3c/NPCM4XX/i3c_master.c +++ b/drivers/i3c/NPCM4XX/i3c_master.c @@ -11,6 +11,10 @@ LOG_MODULE_REGISTER(npcm4xx_i3c_master, CONFIG_I3C_LOG_LEVEL); +extern struct k_work_q npcm4xx_i3c_work_q[I3C_PORT_MAX]; +extern struct k_work work_retry[I3C_PORT_MAX]; +extern struct k_work work_rcv_ibi[I3C_PORT_MAX]; + /** * @brief Callback for I3C master * @param [in] TaskInfo Pointer to the running task @@ -64,6 +68,11 @@ __u32 I3C_Master_Callback(__u32 TaskInfo, __u32 ErrDetail) return I3C_DO_SW_TIMEOUT(pTaskInfo); } + /* + * IBI / Hot-Join / MasterRequest tasks are forked by SLV_START + * So, there is no xfer to return status and data. + * We should define callback functions for these cases. + */ if (ErrDetail == I3C_ERR_IBI) { return I3C_DO_IBI(pTaskInfo); } @@ -427,9 +436,7 @@ __u32 I3C_DO_SW_TIMEOUT(I3C_TASK_INFO_t *pTaskInfo) /*------------------------------------------------------------------------------*/ __u32 I3C_DO_IBI(I3C_TASK_INFO_t *pTaskInfo) { - I3C_TRANSFER_TASK_t *pTask; I3C_BUS_INFO_t *pBus; - I3C_DEVICE_INFO_SHORT_t *pDev; if (pTaskInfo == NULL) { return I3C_ERR_PARAMETER_INVALID; @@ -441,17 +448,13 @@ __u32 I3C_DO_IBI(I3C_TASK_INFO_t *pTaskInfo) return I3C_ERR_PARAMETER_INVALID; } - pTask = pTaskInfo->pTask; - pBus = Get_Bus_From_Port(pTaskInfo->Port); - pDev = GetDevInfoByDynamicAddr(pBus, pTask->address); - if (pDev == NULL) { - return I3C_ERR_TASK_INVALID; - } - if (pTaskInfo->callback != NULL) { /* pTaskInfo->callback(TaskInfo, ErrDetail); */ } + pBus = Get_Bus_From_Port(pTaskInfo->Port); + pBus->pCurrentMaster->bAbort = FALSE; + I3C_Complete_Task(pTaskInfo); pBus->pCurrentTask = NULL; pBus->busState = I3C_BUS_STATE_IDLE; @@ -771,12 +774,24 @@ __u32 I3C_DO_SETAASA(I3C_TASK_INFO_t *pTaskInfo) pDev = pBus->pDevList; while (pDev != NULL) { + /* only SPD5118 support SETAASA */ + if ((pDev->attr.b.present) && /* IS_SPD5118_DEVICE(pDev->pDeviceInfo) */ + (pDev->staticAddr >= 0x50) && (pDev->staticAddr <= 0x57) && + (pDev->attr.b.reqSETAASA)) { + /* SPD5118_DO_SETAASA(pDev->dynamicAddr & 0x07); */ + + pDev->attr.b.runI3C = TRUE; + pDev->dynamicAddr = pDev->staticAddr; + + if ((pDev->attr.b.reqSETAASA) && (pDev->attr.b.doneSETAASA == 0)) + pDev->attr.b.doneSETAASA = 1; + } + pDev = pDev->pNextDev; } /* Remove from master's task */ - pBus->pCurrentTask = NULL; - pBus->busState = I3C_BUS_STATE_INIT; + /* pBus->busState = I3C_BUS_STATE_INIT; */ return I3C_ERR_OK; } @@ -978,7 +993,8 @@ void I3C_Master_Start_Request(__u32 Parm) } else if (I3C_IS_BUS_WAIT_STOP_OR_RETRY(pBus)) { hal_I3C_Process_Task(pTaskInfo); } else { - hal_I3C_Master_Stall(pBus, pTaskInfo->Port); + /* run retry after isr, let slave's isr can complete its task in time */ + /* hal_I3C_Master_Stall(pBus, pTaskInfo->Port); */ if (I3C_IS_BUS_DETECT_SLVSTART(pBus) && (protocol != I3C_TRANSFER_PROTOCOL_EVENT)) { pBus->pCurrentTask = NULL; @@ -1001,6 +1017,8 @@ void I3C_Master_Stop_Request(__u32 Parm) I3C_TRANSFER_TASK_t *pTask; I3C_TASK_INFO_t *pTaskInfo; __u8 port; + I3C_DEVICE_INFO_t *pDevice; + I3C_ErrCode_Enum result; if (Parm == 0) { return; @@ -1017,13 +1035,65 @@ void I3C_Master_Stop_Request(__u32 Parm) } port = pTaskInfo->Port; + pDevice = I3C_Get_INODE(port); + result = pTaskInfo->result; - if (pTaskInfo->result == I3C_ERR_IBI) { + if (result == I3C_ERR_IBI) { pTask->address = hal_I3C_get_ibiAddr(port); } I3C_Master_Callback((uint32_t) pTaskInfo, pTaskInfo->result); hal_I3C_Stop(port); + pDevice->bAbort = FALSE; + + if (result == I3C_ERR_IBI) { + k_work_submit_to_queue(&npcm4xx_i3c_work_q[port], &work_rcv_ibi[port]); + } +} + +/*---------------------------------------------------------------------------------*/ +/** + * @brief Retry master task + * @param [in] Parm Pointer to task + * @return none + */ +/*---------------------------------------------------------------------------------*/ +void I3C_Master_Retry_Frame(__u32 Parm) +{ + I3C_TRANSFER_TASK_t *pTask; + I3C_TRANSFER_FRAME_t *pFrame; + I3C_TASK_INFO_t *pTaskInfo; + + if (Parm == 0) + return; + + pTask = (I3C_TRANSFER_TASK_t *)Parm; + + if (pTask->frame_idx >= pTask->frame_count) + return; + + if (pTask->pTaskInfo == NULL) + return; + + pTaskInfo = pTask->pTaskInfo; + + pFrame = &pTask->pFrameList[pTask->frame_idx]; + if ((pFrame->flag & I3C_TRANSFER_RETRY_ENABLE) && (pFrame->retry_count >= 1)) { + pFrame->retry_count--; + + if (pFrame->flag & I3C_TRANSFER_RETRY_WITHOUT_STOP) { + pFrame->flag |= I3C_TRANSFER_REPEAT_START; + } else { + I3C_Master_Stop_Request((__u32) pTask); + + /* wait a moment for slave to prepare response data */ + /* k_usleep(WAIT_SLAVE_PREPARE_RESPONSE_TIME); */ + } + + I3C_Master_Start_Request((__u32) pTaskInfo); + } else { + I3C_Master_Stop_Request((__u32) pTask); + } } /*------------------------------------------------------------------------------*/ @@ -1130,7 +1200,6 @@ void I3C_Master_Run_Next_Frame(__u32 Parm) pDev = pDev->pNextDev; } } -#undef ASSIGN_NEW_ADDRESS pTask->frame_idx++; I3C_Master_Start_Request((__u32)pTaskInfo); diff --git a/drivers/i3c/NPCM4XX/i3c_slave.c b/drivers/i3c/NPCM4XX/i3c_slave.c index 50f340ce9da973..8d0595dc605943 100644 --- a/drivers/i3c/NPCM4XX/i3c_slave.c +++ b/drivers/i3c/NPCM4XX/i3c_slave.c @@ -11,7 +11,16 @@ #include #include -I3C_REG_ITEM_t *pSlaveReg[I3C_PORT_MAX] = { NULL, NULL }; +I3C_REG_ITEM_t *pSlaveReg[I3C_PORT_MAX] = { + NULL, + NULL, +#if (I3C_PORT_MAX > 2) + NULL, + NULL, + NULL, + NULL, +#endif +}; /*------------------------------------------------------------------------------*/ /** @@ -371,7 +380,7 @@ I3C_ErrCode_Enum Setup_Slave_IBI_DMA(I3C_DEVICE_INFO_t *pDevice) return I3C_ERR_OK; } -void I3C_Update_Dynamic_Address(__u32 Parm) +uint8_t I3C_Update_Dynamic_Address(__u32 Parm) { I3C_PORT_Enum port; I3C_DEVICE_INFO_t *pDevice; @@ -379,7 +388,8 @@ void I3C_Update_Dynamic_Address(__u32 Parm) port = (I3C_PORT_Enum)Parm; pDevice = api_I3C_Get_INODE(port); pDevice->dynamicAddr = hal_i3c_get_dynamic_address(port); - pDevice->bRunI3C = TRUE; + pDevice->bRunI3C = (pDevice->dynamicAddr) ? TRUE : FALSE; + return pDevice->dynamicAddr; } void I3C_Prepare_To_Read_Command(__u32 Parm) diff --git a/drivers/i3c/i3c_npcm4xx.c b/drivers/i3c/i3c_npcm4xx.c index e5f255e01d6b26..3b8d5f7a2f41e2 100644 --- a/drivers/i3c/i3c_npcm4xx.c +++ b/drivers/i3c/i3c_npcm4xx.c @@ -10,6 +10,9 @@ #include #include #include +#include + +#include #include #include @@ -24,7 +27,222 @@ #include "sig_id.h" -LOG_MODULE_REGISTER(npcm4xx_i3c, CONFIG_I3C_LOG_LEVEL); +LOG_MODULE_REGISTER(npcm4xx_i3c_drv, CONFIG_I3C_LOG_LEVEL); + +#define NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE 1024 +#define NPCM4XX_I3C_WORK_QUEUE_PRIORITY -2 +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area0, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area1, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); + +#if (I3C_PORT_MAX == 6) +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area2, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area3, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area4, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); +K_THREAD_STACK_DEFINE(npcm4xx_i3c_stack_area5, NPCM4XX_I3C_WORK_QUEUE_STACK_SIZE); +#endif + +struct k_work_q npcm4xx_i3c_work_q[I3C_PORT_MAX]; + +struct k_work work_stop[I3C_PORT_MAX]; +struct k_work work_next[I3C_PORT_MAX]; +struct k_work work_retry[I3C_PORT_MAX]; +struct k_work work_send_ibi[I3C_PORT_MAX]; +struct k_work work_rcv_ibi[I3C_PORT_MAX]; + +void work_stop_fun(struct k_work *item) +{ + uint8_t i; + I3C_BUS_INFO_t *pBus; + I3C_DEVICE_INFO_t *pDevice; + I3C_TRANSFER_TASK_t *pTask; + + for (i = 0; i < I3C_PORT_MAX; i++) { + if (item == &work_stop[i]) + break; + } + + if (i == I3C_PORT_MAX) + return; + + pDevice = api_I3C_Get_Current_Master_From_Port(i); + if (pDevice == NULL) + return; + + pBus = api_I3C_Get_Bus_From_Port(i); + if (pBus == NULL) + return; + + pTask = pDevice->pTaskListHead; + if (pTask == NULL) + return; + + api_I3C_Master_Stop((uint32_t)pTask); +} + +void work_next_fun(struct k_work *item) +{ + uint8_t i; + I3C_BUS_INFO_t *pBus; + I3C_DEVICE_INFO_t *pDevice; + I3C_TRANSFER_TASK_t *pTask; + + for (i = 0; i < I3C_PORT_MAX; i++) { + if (item == &work_next[i]) + break; + } + + if (i == I3C_PORT_MAX) + return; + + pDevice = api_I3C_Get_Current_Master_From_Port(i); + if (pDevice == NULL) + return; + + pBus = api_I3C_Get_Bus_From_Port(i); + if (pBus == NULL) + return; + + pTask = pDevice->pTaskListHead; + if (pTask == NULL) + return; + + api_I3C_Master_Run_Next_Frame((uint32_t)pTask); +} + +void work_retry_fun(struct k_work *item) +{ + uint8_t i; + I3C_BUS_INFO_t *pBus; + I3C_DEVICE_INFO_t *pDevice; + I3C_TRANSFER_TASK_t *pTask; + + for (i = 0; i < I3C_PORT_MAX; i++) { + if (item == &work_retry[i]) + break; + } + + if (i == I3C_PORT_MAX) + return; + + pDevice = api_I3C_Get_Current_Master_From_Port(i); + if (pDevice == NULL) + return; + + pBus = api_I3C_Get_Bus_From_Port(i); + if (pBus == NULL) + return; + + pTask = pDevice->pTaskListHead; + if (pTask == NULL) + return; + + api_I3C_Master_Retry((uint32_t)pTask); +} + +void work_send_ibi_fun(struct k_work *item) +{ + uint8_t i; + I3C_BUS_INFO_t *pBus; + I3C_DEVICE_INFO_t *pDeviceSlv; + I3C_TRANSFER_TASK_t *pTask; + I3C_TASK_INFO_t *pTaskInfo; + + for (i = 0; i < I3C_PORT_MAX; i++) { + if (item == &work_send_ibi[i]) + break; + } + + if (i == I3C_PORT_MAX) + return; + + pBus = api_I3C_Get_Bus_From_Port(i); + if (pBus == NULL) + return; + + if (pBus->pCurrentTask != NULL) { + k_work_submit_to_queue(&npcm4xx_i3c_work_q[i], item); + return; + } + + pDeviceSlv = api_I3C_Get_INODE(i); + + pTask = pDeviceSlv->pTaskListHead; + if (pTask == NULL) + return; + + pTask = pDeviceSlv->pTaskListHead; + pBus->pCurrentTask = pTask; /* task with higher priority will insert in the front */ + + pTaskInfo = pTask->pTaskInfo; + I3C_Slave_Start_Request((__u32)pTaskInfo); +} + +void work_rcv_ibi_fun(struct k_work *item) +{ + uint8_t i; + I3C_BUS_INFO_t *pBus; + I3C_DEVICE_INFO_t *pDeviceMst; + + I3C_TRANSFER_TASK_t *pTask; + I3C_TASK_INFO_t *pTaskInfo; + + for (i = 0; i < I3C_PORT_MAX; i++) { + if (item == &work_rcv_ibi[i]) + break; + } + + if (i == I3C_PORT_MAX) + return; + + pBus = api_I3C_Get_Bus_From_Port(i); + if (pBus == NULL) + return; + + /* wait until STOP complete */ + if (pBus->pCurrentTask != NULL) { + k_work_submit_to_queue(&npcm4xx_i3c_work_q[i], item); + return; + } + + pDeviceMst = api_I3C_Get_INODE(i); + pTask = pDeviceMst->pTaskListHead; + if (pTask == NULL) + return; + + pTask = pDeviceMst->pTaskListHead; + pBus->pCurrentTask = pTask; /* task with higher priority will insert in the front */ + + pTaskInfo = pTask->pTaskInfo; + I3C_Master_Start_Request((__u32)pTaskInfo); +} + +const struct device *GetDevNodeFromPort(I3C_PORT_Enum port) +{ + switch (port) { + case I3C1_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c0))); + + case I3C2_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c1))); + +#if (I3C_PORT_MAX == 6) + case I3C3_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c2))); + + case I3C4_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c3))); + + case I3C5_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c4))); + + case I3C6_IF: + return device_get_binding(DT_LABEL(DT_NODELABEL(i3c5))); +#endif + + default: + return NULL; + } +} #define I3C_NPCM4XX_CCC_TIMEOUT K_MSEC(10) #define I3C_NPCM4XX_XFER_TIMEOUT K_MSEC(10) @@ -242,18 +460,27 @@ uint32_t PDMA_TxBuf_END[I3C_PORT_MAX] __aligned(4); /*==========================================================================*/ /* - * Customize Register layout to support slave device only + * Customize Register layout */ -#define I3C_REGS_COUNT_PORT 1 +#define I3C_REGS_COUNT_PORT 2 + +#define CMDIdx_MSG 0x01 +#define CMD_BUF_LEN_MSG 64 -#define CMDIdx_MSG 0 -#define CMD_BUF_LEN_MSG 64 +#define CMDIdx_ID 0x0F +#define CMD_BUF_LEN_ID 1 uint8_t I3C_REGs_BUF_CMD_MSG[CMD_BUF_LEN_MSG]; +uint8_t I3C_REGs_BUF_CMD_ID[CMD_BUF_LEN_ID] = { + 0x6C +}; +/* Used to define slave's register table */ I3C_REG_ITEM_t I3C_REGs_PORT_SLAVE[I3C_REGS_COUNT_PORT] = { { .cmd.cmd8 = CMDIdx_MSG, .len = CMD_BUF_LEN_MSG, .buf = I3C_REGs_BUF_CMD_MSG, - .attr.width = 0, .attr.read = TRUE, .attr.write = TRUE }, + .attr.width = 0, .attr.read = TRUE, .attr.write = TRUE }, { + .cmd.cmd8 = CMDIdx_ID, .len = CMD_BUF_LEN_ID, .buf = I3C_REGs_BUF_CMD_ID, + .attr.width = 0, .attr.read = TRUE, .attr.write = FALSE }, }; void hal_I3C_Config_Internal_Device(I3C_PORT_Enum port, I3C_DEVICE_INFO_t *pDevice) @@ -262,6 +489,8 @@ void hal_I3C_Config_Internal_Device(I3C_PORT_Enum port, I3C_DEVICE_INFO_t *pDevi return; if (pDevice == NULL) return; + + /* move to dtsi and i3c_npcm4xx_init() */ } /*--------------------------------------------------------------------------------------*/ @@ -281,6 +510,9 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) if (port >= I3C_PORT_MAX) return I3C_ERR_PARAMETER_INVALID; + if ((pDevice->capability.OFFLINE == FALSE) && (pDevice->bcr & 0x08)) + return I3C_ERR_PARAMETER_INVALID; + result = I3C_ERR_OK; /* SKEW = 0, HKEEP = 3 */ @@ -368,8 +600,8 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) PDMA->SCATBA = (uint32_t) I3C_SCATTER_GATHER_TABLE.SCAT_DSCT; } - I3C_SET_REG_MAXLIMITS(port, I3C_MAXLIMITS_MAXWR(I3C_PAYLOAD_SIZE_MAX) | - I3C_MAXLIMITS_MAXRD(I3C_PAYLOAD_SIZE_MAX)); + hal_I3C_set_MAXRD(port, pDevice->max_rd_len); + hal_I3C_set_MAXWR(port, pDevice->max_wr_len); /* Used for the Mixed bus to send the first START frame */ mconfig &= ~I3C_MCONFIG_ODHPP_MASK; @@ -410,11 +642,13 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) /* BAMATCH */ sconfig &= ~I3C_CONFIG_BAMATCH_MASK; if ((pDevice->capability.IBI) || (pDevice->capability.ASYNC0)) { - sconfig |= I3C_CONFIG_BAMATCH(I3C_CLOCK_SLOW_FREQUENCY / I3C_1MHz_VAL_CONST); + /* prevent slave assert 100 us timeout error */ + /*sconfig |= I3C_CONFIG_BAMATCH(I3C_CLOCK_SLOW_FREQUENCY / I3C_1MHz_VAL_CONST);*/ + sconfig |= I3C_CONFIG_BAMATCH(0x7F); /* if support ASYNC-0, update TCCLOCK */ - I3C_SET_REG_TCCLOCK(port, I3C_TCCLOCK_FREQ(2 * (I3C_CLOCK_FREQUENCY / - I3C_1MHz_VAL_CONST)) | I3C_TCCLOCK_ACCURACY(30)); + /* I3C_SET_REG_TCCLOCK(port, I3C_TCCLOCK_FREQ(2 * (I3C_CLOCK_FREQUENCY / */ + /* I3C_1MHz_VAL_CONST)) | I3C_TCCLOCK_ACCURACY(30)); */ } /* HDRCMD, always enable HDRCMD to detect command process too slow */ @@ -444,22 +678,15 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) sconfig &= ~I3C_CONFIG_NACK_MASK; /* 0: Fixed, 1: Random */ - if (pDevice->pid[0] & 0x80) { + if (pDevice->pid[1] & 0x01) { sconfig |= I3C_CONFIG_IDRAND_MASK; } else { sconfig &= ~I3C_CONFIG_IDRAND_MASK; /* Part ID[15:0] + Instance ID[3:0] + Vendor[11:0] */ I3C_SET_REG_PARTNO(port, pDevice->partNumber); - - pDevice->pid[2] = (uint8_t)(pDevice->partNumber >> 24); - pDevice->pid[3] = (uint8_t)(pDevice->partNumber >> 16); - pDevice->pid[4] = (uint8_t)(pDevice->partNumber >> 8); - pDevice->pid[5] = (uint8_t)(pDevice->partNumber >> 0); } - pDevice->pid[0] = (uint8_t)(pDevice->vendorID >> 7); - pDevice->pid[1] = (uint8_t)(pDevice->vendorID << 1); I3C_SET_REG_IDEXT(port, I3C_GET_REG_IDEXT(port) & ~(I3C_IDEXT_BCR_MASK | I3C_IDEXT_DCR_MASK)); @@ -479,7 +706,7 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) I3C_INTCLR_DACHG_MASK | I3C_INTCLR_TXNOTFULL_MASK | I3C_INTCLR_RXPEND_MASK | I3C_INTCLR_STOP_MASK | I3C_INTCLR_MATCHED_MASK | I3C_INTCLR_START_MASK); - I3C_SET_REG_INTSET(port, I3C_INTSET_CHANDLED_MASK | I3C_INTSET_DDRMATCHED_MASK | + I3C_SET_REG_INTSET(port, /*I3C_INTSET_CHANDLED_MASK | */I3C_INTSET_DDRMATCHED_MASK | I3C_INTSET_ERRWARN_MASK | I3C_INTSET_CCC_MASK | I3C_INTSET_DACHG_MASK | I3C_INTSET_STOP_MASK | I3C_INTSET_START_MASK); @@ -493,13 +720,19 @@ I3C_ErrCode_Enum hal_I3C_Config_Device(I3C_DEVICE_INFO_t *pDevice) } else if (pDevice->mode == I3C_DEVICE_MODE_SLAVE_ONLY) { I3C_SET_REG_MCONFIG(port, mconfig | I3C_MCONFIG_MSTENA(I3C_MCONFIG_MSTENA_MASTER_OFF)); - I3C_SET_REG_CONFIG(port, sconfig | - I3C_CONFIG_SLVENA(I3C_CONFIG_SLVENA_SLAVE_ON)); + + I3C_SET_REG_CONFIG(port, sconfig); + sconfig |= I3C_CONFIG_SLVENA(I3C_CONFIG_SLVENA_SLAVE_ON); + I3C_SET_REG_CONFIG(port, sconfig); + + } else if (pDevice->mode == I3C_DEVICE_MODE_SECONDARY_MASTER) { I3C_SET_REG_MCONFIG(port, mconfig | I3C_MCONFIG_MSTENA(I3C_MCONFIG_MSTENA_MASTER_CAPABLE)); - I3C_SET_REG_CONFIG(port, sconfig | - I3C_CONFIG_SLVENA(I3C_CONFIG_SLVENA_SLAVE_ON)); + + I3C_SET_REG_CONFIG(port, sconfig); + sconfig |= I3C_CONFIG_SLVENA(I3C_CONFIG_SLVENA_SLAVE_ON); + I3C_SET_REG_CONFIG(port, sconfig); } I3C_Enable_Interface(port); @@ -723,6 +956,58 @@ I3C_ErrCode_Enum hal_I3C_disable_interrupt(I3C_PORT_Enum port) return I3C_ERR_OK; } +__u32 hal_I3C_get_MAXRD(I3C_PORT_Enum port) +{ + __u32 regVal; + + if (port >= I3C_PORT_MAX) + return 0; + + regVal = I3C_GET_REG_MAXLIMITS(port); + regVal = (regVal & I3C_MAXLIMITS_MAXRD_MASK) >> I3C_MAXLIMITS_MAXRD_SHIFT; + return regVal; +} + +void hal_I3C_set_MAXRD(I3C_PORT_Enum port, __u32 val) +{ + __u32 regVal; + + if (port >= I3C_PORT_MAX) + return; + + regVal = I3C_GET_REG_MAXLIMITS(port); + regVal &= I3C_MAXLIMITS_MAXWR_MASK; + val = ((val << I3C_MAXLIMITS_MAXRD_SHIFT) & I3C_MAXLIMITS_MAXRD_MASK); + regVal = regVal | val; + I3C_SET_REG_MAXLIMITS(port, regVal); +} + +__u32 hal_I3C_get_MAXWR(I3C_PORT_Enum port) +{ + __u32 regVal; + + if (port >= I3C_PORT_MAX) + return 0; + + regVal = I3C_GET_REG_MAXLIMITS(port); + regVal = (regVal & I3C_MAXLIMITS_MAXWR_MASK) >> I3C_MAXLIMITS_MAXWR_SHIFT; + return regVal; +} + +void hal_I3C_set_MAXWR(I3C_PORT_Enum port, __u32 val) +{ + __u32 regVal; + + if (port >= I3C_PORT_MAX) + return; + + regVal = I3C_GET_REG_MAXLIMITS(port); + regVal &= I3C_MAXLIMITS_MAXRD_MASK; + val = ((val << I3C_MAXLIMITS_MAXWR_SHIFT) & I3C_MAXLIMITS_MAXWR_MASK); + regVal = regVal | val; + I3C_SET_REG_MAXLIMITS(port, regVal); +} + __u32 hal_I3C_get_mstatus(I3C_PORT_Enum port) { return I3C_GET_REG_MSTATUS(port); @@ -1191,9 +1476,12 @@ bool hal_I3C_run_ASYN0(I3C_PORT_Enum port) return FALSE; } -static void CLK_SysTickDelay(uint32_t us) -{ -} +/* + *static void CLK_SysTickDelay(uint32_t us) + *{ + * k_usleep(us); + *} + */ I3C_ErrCode_Enum hal_I3C_Process_Task(I3C_TASK_INFO_t *pTaskInfo) { @@ -1236,6 +1524,8 @@ I3C_ErrCode_Enum hal_I3C_Process_Task(I3C_TASK_INFO_t *pTaskInfo) /* !!! Don't setup Rx DMA in MCTRLDONE, because RX fifo will ORUN */ if (pFrame->direction == I3C_TRANSFER_DIR_READ) { if (protocol == I3C_TRANSFER_PROTOCOL_EVENT) { + /* ack IBI with IBIRULE to prevent 100us TIMEOUT */ + mctrl &= ~I3C_MCTRL_IBIRESP_MASK; rdterm = (pFrame->access_len - pFrame->access_idx); } else if (pFrame->type == I3C_TRANSFER_TYPE_DDR) { rdterm = 2 + (pFrame->access_len - pFrame->access_idx) / 2; @@ -1510,10 +1800,10 @@ void hal_I3C_MemFree(void *pv) k_free(pv); } -static uint32_t tIMG = 400; +/* static uint32_t tIMG = 50; */ void hal_I3C_Master_Stall(I3C_BUS_INFO_t *pBus, I3C_PORT_Enum port) { - CLK_SysTickDelay(tIMG); + /*CLK_SysTickDelay(tIMG);*/ } I3C_ErrCode_Enum hal_I3C_bus_reset(I3C_PORT_Enum Port) @@ -1568,7 +1858,7 @@ void i3c_npcm4xx_gen_stop_to_internal(int inst_id) static int i3c_npcm4xx_init(const struct device *dev); -static uint8_t *pec_append(const struct device *dev, uint8_t *ptr, uint8_t len) +static uint8_t *pec_append(const struct device *dev, uint8_t *ptr, uint16_t len) { struct i3c_npcm4xx_config *config = DEV_CFG(dev); I3C_PORT_Enum port; @@ -1589,6 +1879,31 @@ static uint8_t *pec_append(const struct device *dev, uint8_t *ptr, uint8_t len) return xfer_buf; } +static int pec_valid(const struct device *dev, uint8_t *ptr, uint16_t len) +{ + struct i3c_npcm4xx_config *config; + uint8_t pec_v; + uint8_t address; + uint8_t addr_rnw; + int ret; + + if (len == 0 || ptr == NULL) + return -EINVAL; + + config = DEV_CFG(dev); + + ret = i3c_slave_get_dynamic_addr(dev, &address); + if (ret) + return -EINVAL; + + addr_rnw = address << 1; + + pec_v = crc8_ccitt(0, &addr_rnw, 1); + pec_v = crc8_ccitt(pec_v, ptr, len - 1); + LOG_DBG("pec = %x %x", pec_v, ptr[len - 1]); + return (pec_v == ptr[len - 1]) ? 0 : -EIO; +} + /** * @brief validate slave device exist in the bus ? * @param obj pointer to the bus controller @@ -1613,11 +1928,14 @@ static void i3c_npcm4xx_wr_tx_fifo(struct i3c_npcm4xx_obj *obj, uint8_t *bytes, { struct i3c_npcm4xx_config *config; I3C_PORT_Enum port; + I3C_DEVICE_INFO_t *pDevice; + I3C_ErrCode_Enum ret; config = obj->config; port = config->inst_id; + pDevice = api_I3C_Get_INODE(port); - /* copy data to TX DMA */ + ret = api_I3C_Slave_Prepare_Response(pDevice, nbytes, bytes); } static void i3c_npcm4xx_start_xfer(struct i3c_npcm4xx_obj *obj, struct i3c_npcm4xx_xfer *xfer) @@ -1650,6 +1968,10 @@ static void i3c_npcm4xx_start_xfer(struct i3c_npcm4xx_obj *obj, struct i3c_npcm4 I3C_Slave_Start_Request((__u32)pTaskInfo); k_spin_unlock(&obj->lock, key); + + /* wait until current task complete */ + while (pBus->pCurrentTask) + k_usleep(0); return; } } @@ -1675,11 +1997,11 @@ int i3c_npcm4xx_master_attach_device(const struct device *dev, struct i3c_dev_de I3C_DEVICE_INFO_SHORT_t *pDevInfo = NULL; /* allocate private data of the device */ - priv = (struct i3c_npcm4xx_dev_priv *)k_malloc(sizeof(struct i3c_npcm4xx_dev_priv)); + priv = (struct i3c_npcm4xx_dev_priv *)k_calloc(sizeof(struct i3c_npcm4xx_dev_priv), 1); __ASSERT(priv, "failed to allocat device private data\n"); priv->pos = -1; - priv->ibi.enable = true; + priv->ibi.enable = FALSE; priv->ibi.callbacks = NULL; priv->ibi.context = slave; priv->ibi.incomplete = NULL; @@ -1728,20 +2050,20 @@ int i3c_npcm4xx_master_attach_device(const struct device *dev, struct i3c_dev_de obj->dev_descs[i] = slave; obj->hw_dat_free_pos &= ~BIT(i); } else { - /* slave device must be internal device, and attch */ + /* slave device must be internal device, and match pid */ for (i = 0; i < I3C_PORT_MAX; i++) { pDeviceSlv = api_I3C_Get_INODE(i); - if (pDeviceSlv->pid[0] != (uint8_t) slave->info.pid) + if (pDeviceSlv->pid[5] != (uint8_t) slave->info.pid) continue; - if (pDeviceSlv->pid[1] != (uint8_t)(slave->info.pid >> 8)) + if (pDeviceSlv->pid[4] != (uint8_t)(slave->info.pid >> 8)) continue; - if (pDeviceSlv->pid[2] != (uint8_t)(slave->info.pid >> 16)) + if (pDeviceSlv->pid[3] != (uint8_t)(slave->info.pid >> 16)) continue; - if (pDeviceSlv->pid[3] != (uint8_t)(slave->info.pid >> 24)) + if (pDeviceSlv->pid[2] != (uint8_t)(slave->info.pid >> 24)) continue; - if (pDeviceSlv->pid[4] != (uint8_t)(slave->info.pid >> 32)) + if (pDeviceSlv->pid[1] != (uint8_t)(slave->info.pid >> 32)) continue; - if (pDeviceSlv->pid[5] != (uint8_t)(slave->info.pid >> 48)) + if (pDeviceSlv->pid[0] != (uint8_t)(slave->info.pid >> 48)) continue; pDevInfo = pDeviceSlv->pDevInfo; @@ -1786,6 +2108,7 @@ int i3c_npcm4xx_master_attach_device(const struct device *dev, struct i3c_dev_de lsm6dso.i3c_device.dcr = 0x44; lsm6dso.i3c_device.ackIBI = FALSE; lsm6dso.i3c_device.pReg = NULL; + lsm6dso.i3c_device.regCnt = 0; attr.U16 = 0; attr.b.suppSLV = 1; @@ -1806,6 +2129,51 @@ int i3c_npcm4xx_master_attach_device(const struct device *dev, struct i3c_dev_de lsm6dso.i3c_device.pid, lsm6dso.i3c_device.bcr, lsm6dso.i3c_device.dcr); if (lsm6dso.i3c_device.pDevInfo == NULL) return -ENXIO; + } else if ((slave->info.static_addr >= 0x50) && (slave->info.static_addr <= 0x57)) { + SPD5118_DEVICE_INFO_t spd5118; + I3C_DEVICE_ATTRIB_t attr; + + spd5118.initMode = INITMODE_RSTDAA | INITMODE_SETAASA; + spd5118.state = SPD5118_STATE_DEFAULT; + spd5118.post_init_state = SPD5118_POST_INIT_STATE_Default; + + spd5118.i3c_device.mode = I3C_DEVICE_MODE_SLAVE_ONLY; + spd5118.i3c_device.pOwner = pBus; + spd5118.i3c_device.port = port; + spd5118.i3c_device.bRunI3C = FALSE; + spd5118.i3c_device.staticAddr = slave->info.static_addr; + spd5118.i3c_device.dynamicAddr = I3C_DYNAMIC_ADDR_DEFAULT_7BIT; + spd5118.i3c_device.pid[0] = 0x00; + spd5118.i3c_device.pid[1] = 0x00; + spd5118.i3c_device.pid[2] = 0x00; + spd5118.i3c_device.pid[3] = 0x00; + spd5118.i3c_device.pid[4] = 0x00; + spd5118.i3c_device.pid[5] = 0x00; + spd5118.i3c_device.bcr = 0x00; + spd5118.i3c_device.dcr = 0x00; + spd5118.i3c_device.ackIBI = FALSE; + spd5118.i3c_device.pReg = NULL; + spd5118.i3c_device.regCnt = 0; + + attr.U16 = 0; + attr.b.suppSLV = 1; + attr.b.present = 1; + attr.b.runI3C = 0; + + if (spd5118.initMode & INITMODE_POST_INIT) + attr.b.reqPostInit = 0; + if (spd5118.initMode & INITMODE_RSTDAA) + attr.b.reqRSTDAA = 1; + if (spd5118.initMode & INITMODE_SETAASA) + attr.b.reqSETAASA = 1; + if (spd5118.initMode & INITMODE_ENTDAA) + attr.b.suppENTDAA = 1; + + spd5118.i3c_device.pDevInfo = NewDevInfo(pBus, &spd5118, attr, + spd5118.i3c_device.staticAddr, spd5118.i3c_device.dynamicAddr, + spd5118.i3c_device.pid, spd5118.i3c_device.bcr, spd5118.i3c_device.dcr); + if (spd5118.i3c_device.pDevInfo == NULL) + return -ENXIO; } return 0; @@ -1832,17 +2200,107 @@ int i3c_npcm4xx_master_detach_device(const struct device *dev, struct i3c_dev_de int i3c_npcm4xx_master_request_ibi(struct i3c_dev_desc *i3cdev, struct i3c_ibi_callbacks *cb) { + struct i3c_npcm4xx_dev_priv *priv = DESC_PRIV(i3cdev); + + priv->ibi.callbacks = cb; + priv->ibi.context = i3cdev; + priv->ibi.incomplete = NULL; + return 0; } int i3c_npcm4xx_master_enable_ibi(struct i3c_dev_desc *i3cdev) { - return 0; + struct i3c_npcm4xx_obj *obj = DEV_DATA(i3cdev->bus); + /* struct i3c_register_s *i3c_register = obj->config->base; */ + struct i3c_npcm4xx_dev_priv *priv = DESC_PRIV(i3cdev); + /* union i3c_dev_addr_tbl_s dat; */ + /* union i3c_intr_s intr_reg; */ + /* uint32_t dat_addr, sir_reject; */ + int pos = 0; + + /* pos should be assigned in i3c_master_attach_device() */ + pos = priv->pos; + if (pos < 0) { + return pos; + } + + /* let master accept IBI + * + * sir_reject = i3c_register->sir_reject; + * sir_reject &= ~BIT(pos); + * i3c_register->sir_reject = sir_reject; + */ + + /* update master's setting for slave device's BCR */ + /* dat_addr = (uint32_t)obj->config->base + obj->hw_dat.fields.start_addr + (pos << 2); */ + /* dat.value = sys_read32(dat_addr); */ + /* if (i3cdev->info.bcr & I3C_BCR_IBI_PAYLOAD) dat.fields.ibi_with_data = 1; */ + /* if (obj->hw_feature.ibi_pec_force_enable) dat.fields.ibi_pec_en = 1; */ + /* dat.fields.sir_reject = 0; */ + /* sys_write32(dat.value, dat_addr); */ + + + /* update IBIRULES to receive MDB within 100us */ + /* at most 5 slave devices */ + /* address[7] must be 0 ? */ + I3C_PORT_Enum port; + uint32_t ibirules; + + port = obj->config->inst_id; + ibirules = I3C_GET_REG_IBIRULES(port); + switch (pos) { + case 0: + ibirules &= ~I3C_IBIRULES_ADDR0_MASK; + ibirules |= I3C_IBIRULES_ADDR0(i3cdev->info.dynamic_addr & 0x3F); + break; + case 1: + ibirules &= ~I3C_IBIRULES_ADDR1_MASK; + ibirules |= I3C_IBIRULES_ADDR1(i3cdev->info.dynamic_addr & 0x3F); + break; + case 2: + ibirules &= ~I3C_IBIRULES_ADDR2_MASK; + ibirules |= I3C_IBIRULES_ADDR2(i3cdev->info.dynamic_addr & 0x3F); + break; + case 3: + ibirules &= ~I3C_IBIRULES_ADDR3_MASK; + ibirules |= I3C_IBIRULES_ADDR3(i3cdev->info.dynamic_addr & 0x3F); + break; + case 4: + ibirules &= ~I3C_IBIRULES_ADDR4_MASK; + ibirules |= I3C_IBIRULES_ADDR4(i3cdev->info.dynamic_addr & 0x3F); + break; + default: + LOG_WRN("Can't set address to IBIRULES register !!!"); + return -E2BIG; + } + + I3C_SET_REG_IBIRULES(port, ibirules); + + priv->ibi.enable = 1; + + /* enable ibi interrupt and slave_start detect ? + * + * intr_reg.value = i3c_register->intr_status_en.value; + * intr_reg.fields.ibi_thld = 1; + * i3c_register->intr_status_en.value = intr_reg.value; + * + * intr_reg.value = i3c_register->intr_signal_en.value; + * intr_reg.fields.ibi_thld = 1; + * i3c_register->intr_signal_en.value = intr_reg.value; + */ + + return i3c_master_send_enec(i3cdev->bus, i3cdev->info.dynamic_addr, I3C_CCC_EVT_SIR); } int i3c_npcm4xx_slave_register(const struct device *dev, struct i3c_slave_setup *slave_data) { - struct i3c_npcm4xx_obj *obj = DEV_DATA(dev); + struct i3c_npcm4xx_obj *obj; + + obj = DEV_DATA(dev); + + __ASSERT(slave_data->max_payload_len <= I3C_PAYLOAD_SIZE_MAX, + "msg_size should less than %d.\n", I3C_PAYLOAD_SIZE_MAX); obj->slave_data.max_payload_len = slave_data->max_payload_len; obj->slave_data.callbacks = slave_data->callbacks; @@ -1856,53 +2314,102 @@ int i3c_npcm4xx_slave_register(const struct device *dev, struct i3c_slave_setup int i3c_npcm4xx_slave_put_read_data(const struct device *dev, struct i3c_slave_payload *data, struct i3c_ibi_payload *ibi_notify) { - struct i3c_npcm4xx_config *config = DEV_CFG(dev); - struct i3c_npcm4xx_obj *obj = DEV_DATA(dev); + struct i3c_npcm4xx_config *config; + struct i3c_npcm4xx_obj *obj; I3C_PORT_Enum port; - uint32_t intmasked; - uint32_t ctrl; + uint32_t event_en; + int ret; uint8_t *xfer_buf; - int ret = 0; + I3C_DEVICE_INFO_t *pDevice; __ASSERT_NO_MSG(data); __ASSERT_NO_MSG(data->buf); __ASSERT_NO_MSG(data->size); + config = DEV_CFG(dev); port = config->inst_id; + pDevice = api_I3C_Get_INODE(port); + + obj = DEV_DATA(dev); + + if (config->priv_xfer_pec) { + /* + * uint8_t pec_v; + * uint8_t addr_rnw; + * + * addr_rnw = (uint8_t)I3C_GET_REG_DYNADDR(port) >> 1; + * pec_v = crc8_ccitt(0, &addr_rnw, 1); + * + * xfer_buf = (uint8_t *)&data->buf[1]; + * pec_v = crc8_ccitt(pec_v, xfer_buf, data->size - 1); + * LOG_DBG("pec = %x", pec_v); + * xfer_buf = (uint8_t *)&data->buf[0]; + * xfer_buf[data->size] = pec_v; + * i3c_npcm4xx_wr_tx_fifo(obj, data->buf, data->size + 1); + */ + } else { + i3c_npcm4xx_wr_tx_fifo(obj, data->buf, data->size); + } if (ibi_notify) { - osEventFlagsClear(obj->ibi_event, ~osFlagsError); - intmasked = 0; + if (obj->sir_allowed_by_sw == 0) { + LOG_ERR("SIR is not allowed by software\n"); + return -EACCES; + } + + __ASSERT(ibi_notify->size == 1, "IBI Notify data length Fail !!!\n\n"); + + ret = i3c_slave_get_event_enabling(dev, &event_en); + if (ret || !(event_en & I3C_SLAVE_EVENT_SIR)) { + /* master should polling pending interrupt by GetSTATUS */ + api_I3C_Slave_Update_Pending(pDevice, 0x01); + return 0; + } + + /* osEventFlagsClear(obj->ibi_event, ~osFlagsError); */ - ctrl = I3C_GET_REG_CTRL(port); - ctrl &= ~I3C_CTRL_IBIDATA_MASK; - ctrl |= I3C_CTRL_IBIDATA(ibi_notify->buf[0]); - I3C_SET_REG_CTRL(port, ctrl); + __u16 txlen; + __u16 rxlen = 0; + __u8 TxBuf[2]; + I3C_TRANSFER_PROTOCOL_Enum protocol = I3C_TRANSFER_PROTOCOL_IBI; + __u32 timeout = TIMEOUT_TYPICAL; + + txlen = (uint16_t)ibi_notify->size; /* ibi_notify->size >= 0 */ + TxBuf[0] = ibi_notify->buf[0]; /* MDB */ if (config->ibi_append_pec) { xfer_buf = pec_append(dev, ibi_notify->buf, ibi_notify->size); - i3c_npcm4xx_wr_tx_fifo(obj, xfer_buf, ibi_notify->size + 1); - k_free(xfer_buf); + txlen = 2; + /* i3c_npcm4xx_wr_tx_fifo(obj, xfer_buf, ibi_notify->size + 1); */ + /* k_free(xfer_buf); */ } else { - i3c_npcm4xx_wr_tx_fifo(obj, ibi_notify->buf, ibi_notify->size); + txlen = 1; + /* i3c_npcm4xx_wr_tx_fifo(obj, ibi_notify->buf, ibi_notify->size); */ } - } - osEventFlagsClear(obj->data_event, ~osFlagsError); - if (config->priv_xfer_pec) { - xfer_buf = pec_append(dev, data->buf, data->size); - i3c_npcm4xx_wr_tx_fifo(obj, xfer_buf, data->size + 1); - k_free(xfer_buf); - } else { - i3c_npcm4xx_wr_tx_fifo(obj, data->buf, data->size); + /* let slave drive SLVSTART until bus idle */ + api_I3C_Slave_Create_Task(protocol, txlen, &txlen, &rxlen, TxBuf, NULL, + timeout, NULL, port, NOT_HIF); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[port], &work_send_ibi[port]); } - return ret; + /* + * osEventFlagsClear(obj->data_event, ~osFlagsError); + * if (config->priv_xfer_pec) { + * xfer_buf = pec_append(dev, data->buf, data->size); + * i3c_npcm4xx_wr_tx_fifo(obj, xfer_buf, data->size + 1); + * k_free(xfer_buf); + * } else { + * i3c_npcm4xx_wr_tx_fifo(obj, data->buf, data->size); + * } + */ + + return 0; } int i3c_npcm4xx_slave_send_sir(const struct device *dev, struct i3c_ibi_payload *payload) { - return 0; + return -ENOSYS; } int i3c_npcm4xx_slave_get_dynamic_addr(const struct device *dev, uint8_t *dynamic_addr) @@ -1940,6 +2447,42 @@ int i3c_npcm4xx_slave_get_event_enabling(const struct device *dev, uint32_t *eve return 0; } +static void i3c_npcm4xx_master_rx_ibi(struct i3c_npcm4xx_obj *obj) +{ + struct i3c_dev_desc *i3cdev; + struct i3c_npcm4xx_dev_priv *priv; + struct i3c_ibi_payload *payload; + uint32_t pos; + + I3C_PORT_Enum port; + uint8_t ibi_addr; + I3C_DEVICE_INFO_t *pDevice; + I3C_TRANSFER_TASK_t *pTask; + + port = obj->config->inst_id; + + ibi_addr = hal_I3C_get_ibiAddr(port); + pos = i3c_npcm4xx_get_pos(obj, ibi_addr); + if (pos < 0) { + LOG_ERR("unregistered IBI source: 0x%x\n", ibi_addr); + return; + } + + i3cdev = obj->dev_descs[pos]; + priv = DESC_PRIV(i3cdev); + + /* get return data structure, payload from private data */ + payload = priv->ibi.callbacks->write_requested(priv->ibi.context); + + pDevice = api_I3C_Get_INODE(port); + pTask = pDevice->pTaskListHead; + payload->size = *pTask->pRdLen; + memcpy(payload->buf, pTask->pRdBuf, payload->size); + + /* notify, ibi task has been processed */ + priv->ibi.callbacks->write_done(priv->ibi.context); +} + union i3c_device_cmd_queue_port_s { volatile uint32_t value; @@ -2016,7 +2559,6 @@ int i3c_npcm4xx_master_send_ccc(const struct device *dev, struct i3c_ccc_cmd *cc struct i3c_npcm4xx_cmd cmd; union i3c_device_cmd_queue_port_s cmd_hi, cmd_lo; int pos = 0; - int ret; /* To construct task */ __u8 CCC; @@ -2233,8 +2775,10 @@ int i3c_npcm4xx_master_send_ccc(const struct device *dev, struct i3c_ccc_cmd *cc /* wait done, xfer.ret will be changed in ISR */ k_sem_take(&xfer.sem, I3C_NPCM4XX_CCC_TIMEOUT); - ret = xfer.ret; - return ret; + + /* stop worker thread*/ + + return xfer.ret; } /* i3cdev -> target device */ @@ -2435,7 +2979,12 @@ int i3c_npcm4xx_master_send_entdaa(struct i3c_dev_desc *i3cdev) i3c_npcm4xx_start_xfer(obj, &xfer); /* wait done, xfer.ret will be changed in ISR */ - k_sem_take(&xfer.sem, I3C_NPCM4XX_CCC_TIMEOUT); + if (k_sem_take(&xfer.sem, I3C_NPCM4XX_CCC_TIMEOUT) == 0) { + if (xfer.ret == 0) { + i3cdev->info.i2c_mode = 0; + /*i3cdev->info.dynamic_addr = i3cdev->info.assigned_dynamic_addr;*/ + } + } return 0; } @@ -2453,6 +3002,17 @@ uint32_t I3C_Slave_Register_Access(I3C_PORT_Enum port, uint16_t rx_cnt, uint8_t #define ENTER_SLAVE_ISR() { /* GPIO_Set_Data(GPIOC, 5, GPIO_DATA_LOW); */ } #define EXIT_SLAVE_ISR() { /* GPIO_Set_Data(GPIOC, 5, GPIO_DATA_HIGH); */ } + +/* MDMA */ +#define I3C_MDMA_STOP_TX(p) {\ + I3C_SET_REG_MDMACTRL(p, I3C_GET_REG_MDMACTRL(p) & ~I3C_MDMACTRL_DMATB_MASK); } + +#define I3C_MDMA_FLUSH_TX(p) {\ + I3C_SET_REG_MDATACTRL(p, I3C_GET_REG_MDATACTRL(p) | I3C_MDATACTRL_FLUSHTB_MASK); } + +#define UpdateTaskInfoResult(t, r) { t->result = r; } +#define UpdateTaskResult(t, r) { UpdateTaskInfoResult(t->pTaskInfo, r); } + void I3C_Master_ISR(uint8_t I3C_IF) { I3C_TASK_INFO_t *pTaskInfo; @@ -2475,6 +3035,9 @@ void I3C_Master_ISR(uint8_t I3C_IF) pBus = api_I3C_Get_Bus_From_Port(I3C_IF); mintmask = I3C_GET_REG_MINTMASKED(I3C_IF); + if (mintmask == 0) + return; + if (mintmask & I3C_MINTMASKED_ERRWARN_MASK) { merrwarn = I3C_GET_REG_MERRWARN(I3C_IF); I3C_SET_REG_MERRWARN(I3C_IF, merrwarn); @@ -2485,28 +3048,31 @@ void I3C_Master_ISR(uint8_t I3C_IF) case I3C_MERRWARN_NACK_MASK: mstatus = I3C_GET_REG_MSTATUS(I3C_IF); if ((mstatus & I3C_MSTATUS_STATE_MASK) == I3C_MSTATUS_STATE_DAA) { - pTask->pTaskInfo->result = I3C_ERR_NACK; + UpdateTaskResult(pTask, I3C_ERR_NACK); } else { mstatus = I3C_GET_REG_MSTATUS(I3C_IF); I3C_SET_REG_MSTATUS(I3C_IF, mstatus | I3C_MSTATUS_COMPLETE_MASK); if (mstatus & I3C_MSTATUS_SLVSTART_MASK) { I3C_SET_REG_MSTATUS(I3C_IF, I3C_MSTATUS_SLVSTART_MASK); - - I3C_SET_REG_MDMACTRL(I3C_IF, I3C_GET_REG_MDMACTRL(I3C_IF) & - ~I3C_MDMACTRL_DMATB_MASK); - I3C_SET_REG_MDATACTRL(I3C_IF, - I3C_GET_REG_MDATACTRL(I3C_IF) | - I3C_MDATACTRL_FLUSHTB_MASK); - - pTask->pTaskInfo->result = I3C_ERR_SLVSTART; - I3C_SET_REG_MINTSET(I3C_IF, I3C_MINTSET_SLVSTART_MASK); - api_I3C_Master_Stop((uint32_t) pTask); - } else { - pTask->pTaskInfo->result = I3C_ERR_NACK; - api_I3C_Master_Stop((uint32_t) pTask); + I3C_MDMA_STOP_TX(I3C_IF); + I3C_MDMA_FLUSH_TX(I3C_IF); + UpdateTaskResult(pTask, I3C_ERR_SLVSTART); + I3C_SET_REG_MINTSET(I3C_IF, + I3C_MINTSET_SLVSTART_MASK); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_stop[I3C_IF]); + } else if (pTask->pFrameList[pTask->frame_idx].direction + == I3C_TRANSFER_DIR_WRITE) { + UpdateTaskResult(pTask, I3C_ERR_NACK); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; + } else { + UpdateTaskResult(pTask, I3C_ERR_NACK); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_retry[I3C_IF]); } } break; @@ -2519,7 +3085,7 @@ void I3C_Master_ISR(uint8_t I3C_IF) I3C_GET_REG_MDMACTRL(I3C_IF) & ~I3C_MDMACTRL_DMATB_MASK); I3C_SET_REG_MDATACTRL(I3C_IF, I3C_GET_REG_MDATACTRL(I3C_IF) | I3C_MDATACTRL_FLUSHTB_MASK); - api_I3C_Master_Stop((uint32_t) pDevice->pTaskListHead); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], &work_stop[I3C_IF]); break; default: @@ -2568,10 +3134,13 @@ void I3C_Master_ISR(uint8_t I3C_IF) if (ibi_type == I3C_MSTATUS_IBITYPE_IBI) { mctrl = I3C_GET_REG_MCTRL(I3C_IF); if ((mctrl & I3C_MCTRL_IBIRESP_MASK) == I3C_MCTRL_IBIRESP(0x01)) { + /* NaK IBI */ I3C_SET_REG_MSTATUS(I3C_IF, I3C_MSTATUS_IBIWON_MASK | I3C_MSTATUS_COMPLETE_MASK | I3C_MSTATUS_MCTRLDONE_MASK); mintmask = I3C_MINTMASKED_MCTRLDONE_MASK; } else if (ibi_type == I3C_MSTATUS_IBITYPE_IBI) { + /* ACK with MDB */ + I3C_SET_REG_MSTATUS(I3C_IF, I3C_MSTATUS_MCTRLDONE_MASK); pTaskInfo->result = I3C_ERR_IBI; } @@ -2611,7 +3180,8 @@ void I3C_Master_ISR(uint8_t I3C_IF) if (mstatus & I3C_MSTATUS_BETWEEN_MASK) { pTaskInfo->result = I3C_ERR_OK; - api_I3C_Master_Stop((uint32_t) pDevice->pTaskListHead); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_stop[I3C_IF]); } } else { while ((pTask->pFrameList[pTask->frame_idx + 1].access_idx < @@ -2622,7 +3192,8 @@ void I3C_Master_ISR(uint8_t I3C_IF) pTask->pFrameList[pTask->frame_idx + 1].access_idx++; } - api_I3C_Master_Run_Next_Frame((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_next[I3C_IF]); } } else if (pTask->protocol == I3C_TRANSFER_PROTOCOL_ENTDAA) { /* no slave want to participate ENTDAA, but slave ack 7E+Wr @@ -2633,11 +3204,14 @@ void I3C_Master_ISR(uint8_t I3C_IF) if (pFrame->access_len == 0) { /* 7E+Wr / CCCw */ if (pTask->frame_count == (pTask->frame_idx + 1)) { - api_I3C_Master_Stop((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; } - api_I3C_Master_Run_Next_Frame((uint32_t) pTask); + + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_next[I3C_IF]); } else { mstatus = I3C_GET_REG_MSTATUS(I3C_IF); if (mstatus & I3C_MSTATUS_SLVSTART_MASK) { @@ -2704,23 +3278,25 @@ void I3C_Master_ISR(uint8_t I3C_IF) /* Error has been caught, but complete also assert */ if (pTaskInfo->result == I3C_ERR_WRABT) { - api_I3C_Master_Stop((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; } if (pTaskInfo->result == I3C_ERR_IBI) { - /* slvRxOffset[I3C_IF] should less than 69 */ - slvRxOffset[I3C_IF] = *pTask->pRdLen; - memcpy(&slvRxBuf[I3C_IF][0], pTask->pRdBuf, slvRxOffset[I3C_IF]); + const struct device *dev; + struct i3c_npcm4xx_obj *obj; - api_I3C_Master_Stop((uint32_t) pTask); + dev = GetDevNodeFromPort(I3C_IF); + obj = DEV_DATA(dev); + i3c_npcm4xx_master_rx_ibi(obj); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; } if (pTaskInfo->result == I3C_ERR_MR) { - api_I3C_Master_Stop((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; } @@ -2743,7 +3319,8 @@ void I3C_Master_ISR(uint8_t I3C_IF) } if (pFrame->flag & I3C_TRANSFER_NO_STOP) { - api_I3C_Master_Run_Next_Frame((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], + &work_next[I3C_IF]); EXIT_MASTER_ISR(); return; } @@ -2771,7 +3348,7 @@ void I3C_Master_ISR(uint8_t I3C_IF) } } - api_I3C_Master_Stop((uint32_t) pTask); + k_work_submit_to_queue(&npcm4xx_i3c_work_q[I3C_IF], &work_stop[I3C_IF]); EXIT_MASTER_ISR(); return; } @@ -2805,8 +3382,13 @@ void I3C_Master_ISR(uint8_t I3C_IF) if (I3C_GET_REG_MINTMASKED(I3C_IF) & I3C_MINTMASKED_SLVSTART_MASK) { I3C_SET_REG_MSTATUS(I3C_IF, I3C_MSTATUS_SLVSTART_MASK); - if (pBus->pCurrentTask != NULL) + if ((pBus->pCurrentTask != NULL) && + (pBus->pCurrentTask->protocol != I3C_TRANSFER_PROTOCOL_IBI) && + (pBus->pCurrentTask->protocol != I3C_TRANSFER_PROTOCOL_MASTER_REQUEST) && + (pBus->pCurrentTask->protocol != I3C_TRANSFER_PROTOCOL_HOT_JOIN)) { pDevice->bAbort = TRUE; + } + api_I3C_Master_New_Request((uint32_t)I3C_IF); } @@ -2821,13 +3403,14 @@ void I3C_Slave_ISR(uint8_t I3C_IF) I3C_TRANSFER_FRAME_t *pFrame; uint32_t intmasked; uint8_t evdet; - uint32_t tmp; uint32_t errwarn; ENTER_SLAVE_ISR(); pDevice = api_I3C_Get_INODE(I3C_IF); intmasked = I3C_GET_REG_INTMASKED(I3C_IF); + if (intmasked == 0) + return; if (intmasked & I3C_INTMASKED_START_MASK) { evdet = (uint8_t)((I3C_GET_REG_STATUS(I3C_IF) & I3C_STATUS_EVDET_MASK) >> @@ -2843,6 +3426,16 @@ void I3C_Slave_ISR(uint8_t I3C_IF) } } + /* + * for RESTART + * 1. We must terminate previous RX DMA / FIFO before master send "Index" / Data + * 2. We must init RX DMA to get "Index" and data + */ + if (intmasked & I3C_INTMASKED_STOP_MASK) { + /* update tx buffer here, only when slave doesn't get STOP */ + I3C_Slave_Handle_DMA((uint32_t)pDevice); + } + I3C_SET_REG_STATUS(I3C_IF, I3C_STATUS_START_MASK); } @@ -2852,10 +3445,12 @@ void I3C_Slave_ISR(uint8_t I3C_IF) } if (intmasked & I3C_INTMASKED_CCC_MASK) { - I3C_SET_REG_STATUS(I3C_IF, I3C_STATUS_CCC_MASK); + /* Can't reset here, will validate CCC in I3C_Slave_Handle_DMA() */ + /* I3C_SET_REG_STATUS(I3C_IF, I3C_STATUS_CCC_MASK); */ } if (intmasked & I3C_INTMASKED_DDRMATCHED_MASK) { +/* tmp = I3C_Slave_Register_Access(I3C_IF, slvRxOffset[I3C_IF], slvRxBuf[I3C_IF], TRUE); @@ -2864,6 +3459,7 @@ void I3C_Slave_ISR(uint8_t I3C_IF) } else { I3C_SET_REG_STATUS(I3C_IF, I3C_STATUS_DDRMATCH_MASK); } +*/ } if (intmasked & I3C_INTMASKED_RXPEND_MASK) { @@ -2894,35 +3490,35 @@ void I3C_Slave_ISR(uint8_t I3C_IF) if (errwarn & I3C_ERRWARN_OWRITE_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_OWRITE_MASK); - LOG_INF("@E0"); + LOG_WRN("@E0"); } if (errwarn & I3C_ERRWARN_OREAD_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_OREAD_MASK); - LOG_INF("@E1"); + LOG_WRN("@E1"); } if (errwarn & I3C_ERRWARN_HCRC_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_HCRC_MASK); - LOG_INF("@E3"); + LOG_WRN("@E3"); } if (errwarn & I3C_ERRWARN_HPAR_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_HPAR_MASK); - LOG_INF("@E4"); + LOG_WRN("@E4"); } if (errwarn & I3C_ERRWARN_ORUN_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_ORUN_MASK); - LOG_INF("@E5"); + LOG_WRN("@E5"); } if (errwarn & I3C_ERRWARN_TERM_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_TERM_MASK); } if (errwarn & I3C_ERRWARN_S0S1_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_S0S1_MASK); - LOG_INF("@E7"); + LOG_WRN("@E7"); } if (errwarn & I3C_ERRWARN_URUN_MASK) { I3C_SET_REG_ERRWARN(I3C_IF, I3C_ERRWARN_URUN_MASK); - LOG_INF("@EA"); + LOG_WRN("@EA"); } } @@ -2985,7 +3581,19 @@ void I3C_Slave_ISR(uint8_t I3C_IF) } if (I3C_GET_REG_STATUS(I3C_IF) & I3C_STATUS_DACHG_MASK) { - I3C_Update_Dynamic_Address((uint32_t) I3C_IF); + LOG_DBG("dynamic address assigned\n"); + + uint8_t addr; + const struct device *dev; + struct i3c_npcm4xx_obj *obj; + + addr = I3C_Update_Dynamic_Address((uint32_t) I3C_IF); + if (addr) { + dev = GetDevNodeFromPort(I3C_IF); + obj = DEV_DATA(dev); + k_work_submit(&obj->work); + } + I3C_SET_REG_STATUS(I3C_IF, I3C_STATUS_DACHG_MASK); } @@ -3000,7 +3608,125 @@ void I3C_Slave_ISR(uint8_t I3C_IF) void I3C_Slave_Handle_DMA(__u32 Parm) { + I3C_DEVICE_INFO_t *pDevice; + I3C_PORT_Enum port; + uint16_t txDataLen; + bool bRet; + + pDevice = (I3C_DEVICE_INFO_t *)Parm; + if (pDevice == NULL) + return; + + port = pDevice->port; + + /* Slave Rcv data ? + * 1. Rx DMA is started, and TXCNT change + * 2. DDR matched + * 3. vendor CCC, not implement yet + */ + if ((I3C_GET_REG_STATUS(port) & I3C_STATUS_DDRMATCH_MASK) || + ((I3C_GET_REG_DMACTRL(port) & I3C_DMACTRL_DMAFB_MASK))) { + /* Update receive data length */ + if (PDMA->TDSTS & MaskBit(port + I3C_PORT_MAX)) { + /* PDMA Rx Task Done */ + PDMA->TDSTS = MaskBit(port + I3C_PORT_MAX); + slvRxOffset[port] = slvRxLen[port]; + + /* receive data more than DMA buffer size -> overrun & drop */ + if (I3C_GET_REG_DATACTRL(port) & I3C_DATACTRL_RXCOUNT_MASK) { + I3C_SET_REG_DATACTRL(port, I3C_GET_REG_DATACTRL(port) + | I3C_DATACTRL_FLUSHFB_MASK); + LOG_WRN("Increase buffer size or limit transfer size !!!\r\n"); + } + } else { + /* PDMA Rx Task not finish */ + slvRxOffset[port] = slvRxLen[port] - (((PDMA->DSCT[I3C_PORT_MAX + + port].CTL & PDMA_DSCT_CTL_TXCNT_Msk) >> PDMA_DSCT_CTL_TXCNT_Pos) + + 1); + } + + /* Process the Rcvd Data */ + if (slvRxOffset[port]) { + /* Stop RX DMA */ + I3C_SET_REG_DMACTRL(port, I3C_GET_REG_DMACTRL(port) & + ~I3C_DMACTRL_DMAFB_MASK); + PDMA->CHCTL &= ~MaskBit(port + I3C_PORT_MAX); + + if (I3C_GET_REG_STATUS(port) & I3C_STATUS_CCC_MASK) { + /* reserved for vendor CCC, drop rx data directly */ + if (slvRxBuf[port][0] == CCC_BROADCAST_SETAASA) { + LOG_WRN("rcv setaasa..."); + } + + /* we can't support SETAASA because DYNADDR is RO. */ + slvRxOffset[port] = 0; + I3C_SET_REG_STATUS(port, I3C_GET_REG_STATUS(port) | + I3C_STATUS_CCC_MASK); + } else { + bRet = FALSE; + + /* To fill rx data to the requested mqueue */ + /* We must find callback from slave_data */ + const struct device *dev; + struct i3c_npcm4xx_obj *obj; + struct i3c_slave_payload *payload; + int ret; + dev = GetDevNodeFromPort(port); + if ((dev == NULL) || !device_is_ready(dev)) + return; + + /* slave device */ + obj = DEV_DATA(dev); + + /* prepare m_queue to backup rx data */ + if (obj->slave_data.callbacks->write_requested != NULL) { + payload = obj->slave_data.callbacks->write_requested( + obj->slave_data.dev); + payload->size = slvRxOffset[port]; + + /*i3c_aspeed_rd_rx_fifo(obj, payload->buf, payload->size);*/ + bRet = TRUE; + if (obj->config->priv_xfer_pec) { + ret = pec_valid(dev, (uint8_t *)&slvRxBuf[port], + slvRxOffset[port]); + if (ret) { + LOG_WRN("PEC error\n"); + bRet = FALSE; + payload->size = 0; + } + } + + memcpy(payload->buf, slvRxBuf[port], payload->size); + } + + if (obj->slave_data.callbacks->write_done != NULL) { + obj->slave_data.callbacks->write_done(obj->slave_data.dev); + } + } + } + } + + /* Slave TX data has send ? */ + txDataLen = 0; + if (I3C_GET_REG_DMACTRL(port) & I3C_DMACTRL_DMATB_MASK) { + /* Response data still not move to Tx FIFO */ + txDataLen = ((PDMA->DSCT[port].CTL & PDMA_DSCT_CTL_TXCNT_Msk) >> + PDMA_DSCT_CTL_TXCNT_Pos) + 1; + } + + /* Response data still in Tx FIFO */ + txDataLen += (I3C_GET_REG_DATACTRL(port) & I3C_DATACTRL_TXCOUNT_MASK) >> + I3C_DATACTRL_TXCOUNT_SHIFT; + + if (pDevice->txLen != 0) { + if (txDataLen == 0) { + /* call tx send complete hook */ + api_I3C_Slave_Finish_Response(pDevice); + } else { + /* do nothing, we can get correct tx len again */ + } + } } I3C_ErrCode_Enum GetRegisterIndex(I3C_DEVICE_INFO_t *pDevice, uint16_t rx_cnt, uint8_t *pRx_buff, @@ -3015,7 +3741,7 @@ I3C_ErrCode_Enum GetRegisterIndex(I3C_DEVICE_INFO_t *pDevice, uint16_t rx_cnt, u return I3C_ERR_PARAMETER_INVALID; *pIndexRet = 0; - reg_count = sizeof(pDevice->pReg) / sizeof(I3C_REG_ITEM_t *); + reg_count = pDevice->regCnt; reg_chk_count = 0; for (i = 0; i < reg_count; i++) { @@ -3070,7 +3796,7 @@ static void i3c_npcm4xx_isr(const struct device *dev) sconfig = sys_read32(I3C_BASE_ADDR(port) + OFFSET_CONFIG); if ((sconfig & I3C_CONFIG_SLVENA_MASK) == I3C_CONFIG_SLVENA_SLAVE_ON) { - I3C_Slave_ISR(I3C1_IF); + I3C_Slave_ISR(port); return; } } @@ -3096,13 +3822,14 @@ static void i3c_npcm4xx_isr(const struct device *dev) uint32_t I3C_Slave_Register_Access(I3C_PORT_Enum port, uint16_t rx_cnt, uint8_t *pRx_buff, bool bHDR) { - I3C_DEVICE_INFO_t *pDevice = api_I3C_Get_INODE(port); + I3C_DEVICE_INFO_t *pDevice; uint8_t cmd_id, hdrCmd1, hdrCmd2; I3C_ErrCode_Enum result; uint32_t tmp32; - hdrCmd1 = 0; + pDevice = api_I3C_Get_INODE(port); + hdrCmd1 = 0; if (bHDR) { tmp32 = I3C_GET_REG_HDRCMD(port); hdrCmd1 = (uint8_t)(tmp32 & I3C_HDRCMD_CMD0_MASK); @@ -3182,12 +3909,69 @@ uint32_t I3C_Slave_Register_Access(I3C_PORT_Enum port, uint16_t rx_cnt, uint8_t return 0; } +static int i3c_init_work_queue(I3C_PORT_Enum port) +{ + switch (port) { + case 0: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area0, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area0), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; + case 1: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area1, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area1), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; +#if (I3C_PORT_MAX == 6) + case 2: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area2, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area2), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; + case 3: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area3, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area3), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; + case 4: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area4, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area4), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; + case 5: + k_work_queue_start(&npcm4xx_i3c_work_q[port], npcm4xx_i3c_stack_area5, + K_THREAD_STACK_SIZEOF(npcm4xx_i3c_stack_area5), + NPCM4XX_I3C_WORK_QUEUE_PRIORITY, NULL); + break; +#endif + default: + return -ENXIO; + } + + k_work_init(&work_stop[port], work_stop_fun); + k_work_init(&work_next[port], work_next_fun); + k_work_init(&work_retry[port], work_retry_fun); + k_work_init(&work_send_ibi[port], work_send_ibi_fun); + k_work_init(&work_rcv_ibi[port], work_rcv_ibi_fun); + + return 0; +} + +static void sir_allowed_worker(struct k_work *work) +{ + struct i3c_npcm4xx_obj *obj = CONTAINER_OF(work, struct i3c_npcm4xx_obj, work); + + /* k_msleep(1000); */ + obj->sir_allowed_by_sw = 1; +} + static int i3c_npcm4xx_init(const struct device *dev) { struct i3c_npcm4xx_config *config = DEV_CFG(dev); struct i3c_npcm4xx_obj *obj = DEV_DATA(dev); I3C_PORT_Enum port = config->inst_id; I3C_DEVICE_INFO_t *pDevice; + int ret; LOG_INF("size_t=%d, uint32_t=%d\n", sizeof(size_t), sizeof(uint32_t)); LOG_INF("Base=%x\n", (uint32_t) config->base); @@ -3202,6 +3986,9 @@ static int i3c_npcm4xx_init(const struct device *dev) obj->config = config; obj->hw_dat_free_pos = GENMASK(DEVICE_COUNT_MAX - 1, 0); + ret = i3c_init_work_queue(port); + __ASSERT(ret == 0, "failed to init work queue for i3c driver !!!"); + /* update default setting */ I3C_Port_Default_Setting(port); @@ -3211,18 +3998,25 @@ static int i3c_npcm4xx_init(const struct device *dev) /* Update device node by user setting */ pDevice->disableTimeout = TRUE; pDevice->vendorID = I3C_GET_REG_VENDORID(port); + pDevice->partNumber = (uint32_t)config->part_id << 16 | + (uint32_t)port << 12 | /* instance id */ + (uint32_t)config->vendor_def_id; /* vendor def id*/ pDevice->bcr = config->bcr; pDevice->dcr = config->dcr; - pDevice->pid[0] = 0x00; - pDevice->pid[1] = ((port & 0x0F) << 4) | 0x00; - pDevice->pid[2] = 0x00; - pDevice->pid[3] = 0x00; - pDevice->pid[4] = (uint8_t)(pDevice->vendorID << 1); - pDevice->pid[5] = (uint8_t)(pDevice->vendorID >> 7); + pDevice->pid[0] = (uint8_t)(pDevice->vendorID >> 7); + pDevice->pid[1] = (uint8_t)(pDevice->vendorID << 1); + pDevice->pid[2] = (uint8_t)(config->part_id >> 8); + pDevice->pid[3] = (uint8_t)config->part_id; + pDevice->pid[4] = ((port & 0x0F) << 4) | + ((uint8_t)(config->vendor_def_id >> 8) & 0x0F); + pDevice->pid[5] = (uint8_t)config->vendor_def_id; pDevice->staticAddr = config->assigned_addr; + pDevice->max_rd_len = I3C_PAYLOAD_SIZE_MAX; + pDevice->max_wr_len = I3C_PAYLOAD_SIZE_MAX; + if (config->slave) { if (config->secondary) pDevice->mode = I3C_DEVICE_MODE_SECONDARY_MASTER; @@ -3230,6 +4024,16 @@ static int i3c_npcm4xx_init(const struct device *dev) pDevice->mode = I3C_DEVICE_MODE_SLAVE_ONLY; pDevice->callback = I3C_Slave_Callback; + + pDevice->stopSplitRead = FALSE; + pDevice->capability.OFFLINE = FALSE; + + obj->sir_allowed_by_sw = 0; + k_work_init(&obj->work, sir_allowed_worker); + + /* for loopback test without ibi behavior only */ + pDevice->pReg = I3C_REGs_PORT_SLAVE; + pDevice->regCnt = sizeof(I3C_REGs_PORT_SLAVE) / sizeof(I3C_REG_ITEM_t); } else { pDevice->mode = I3C_DEVICE_MODE_CURRENT_MASTER; @@ -3253,6 +4057,8 @@ static int i3c_npcm4xx_init(const struct device *dev) .secondary = DT_INST_PROP_OR(n, secondary, 0),\ .bcr = DT_INST_PROP_OR(n, bcr, 0),\ .dcr = DT_INST_PROP_OR(n, dcr, 0),\ + .part_id = DT_INST_PROP_OR(n, part_id, 0),\ + .vendor_def_id = DT_INST_PROP_OR(n, vendor_def_id, 0),\ .busno = DT_INST_PROP_OR(n, busno, I3C_BUS_COUNT_MAX),\ .i2c_scl_hz = DT_INST_PROP_OR(n, i2c_scl_hz, 0),\ .i3c_scl_hz = DT_INST_PROP_OR(n, i3c_scl_hz, 0),\ diff --git a/drivers/i3c/i3c_shell.c b/drivers/i3c/i3c_shell.c new file mode 100644 index 00000000000000..8a453f9cc4b8f4 --- /dev/null +++ b/drivers/i3c/i3c_shell.c @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2021 ASPEED Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#define I3C_DEVICE_PREFIX "I3C_" +#define I3C_SHELL_MAX_XFER_NUM 2 +#define I3C_SHELL_MAX_BUF_SIZE 16 +#define I3C_SHELL_MAX_DESC_NUM 8 + +static uint8_t data_buf[I3C_SHELL_MAX_XFER_NUM][I3C_SHELL_MAX_BUF_SIZE]; +static int i3c_shell_num_of_descs; +static struct i3c_dev_desc i3c_shell_desc_tbl[I3C_SHELL_MAX_DESC_NUM]; + +static void device_name_get(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, I3C_DEVICE_PREFIX); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} +SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); + +static struct i3c_dev_desc *find_matching_desc(const struct device *dev, uint8_t desc_addr) +{ + struct i3c_dev_desc *desc = NULL; + int i; + + for (i = 0; i < I3C_SHELL_MAX_DESC_NUM; i++) { + if (i3c_shell_desc_tbl[i].bus == dev && + i3c_shell_desc_tbl[i].info.dynamic_addr == desc_addr) { + desc = &i3c_shell_desc_tbl[i]; + break; + } + } + + return desc; +} + +static uint32_t args_to_wdata(char *arg, uint8_t *buf) +{ + char *data_ptrs[I3C_SHELL_MAX_BUF_SIZE]; + char *state; + int i = 0, len = 0; + + data_ptrs[i] = strtok_r(arg, ",", &state); + while (data_ptrs[i] && i < I3C_SHELL_MAX_BUF_SIZE - 1) { + data_ptrs[++i] = strtok_r(NULL, ",", &state); + } + + for (len = 0; len < i; len++) { + buf[len] = strtoul(data_ptrs[len], NULL, 0); + } + + return len; +} + +static const char priv_xfer_helper[] = "i3c xfer -a -w -r "; +static int cmd_priv_xfer(const struct shell *shell, size_t argc, char **argv) +{ + const struct device *dev; + struct i3c_priv_xfer xfers[I3C_SHELL_MAX_XFER_NUM]; + struct i3c_dev_desc *desc; + struct getopt_state *state; + int nxfers = 0; + int addr = -1; + int c, ret; + + dev = device_get_binding(argv[1]); + if (!dev) { + shell_error(shell, "I3C: Device %s not found.", argv[1]); + return -ENODEV; + } + + while ((c = shell_getopt(shell, argc - 1, &argv[1], "ha:w:r:")) != -1) { + state = shell_getopt_state_get(shell); + switch (c) { + case 'a': + addr = strtoul(state->optarg, NULL, 0); + break; + case 'w': + xfers[nxfers].rnw = 0; + xfers[nxfers].data.out = data_buf[nxfers]; + xfers[nxfers].len = args_to_wdata(state->optarg, data_buf[nxfers]); + nxfers++; + break; + case 'r': + xfers[nxfers].rnw = 1; + xfers[nxfers].data.in = data_buf[nxfers]; + xfers[nxfers].len = atoi(state->optarg); + nxfers++; + break; + case 'h': + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + case '?': + if ((state->optopt == 'a') || (state->optopt == 'w') || + (state->optopt == 'r')) { + shell_print(shell, "Option -%c requires an argument.", + state->optopt); + } else if (isprint(state->optopt)) { + shell_print(shell, "Unknown option `-%c'.", state->optopt); + } else { + shell_print(shell, "Unknown option character `\\x%x'.", + state->optopt); + } + return 1; + default: + break; + } + } + + if (addr < 0) { + shell_error(shell, "I3C: Slave not assigned."); + return -ENODEV; + } + + desc = find_matching_desc(dev, addr); + if (!desc) { + shell_error(shell, "I3C: Slave %x not found.", addr); + return -ENODEV; + } + + shell_print(shell, "Private transfer to address 0x%02x\n", addr); + ret = i3c_master_priv_xfer(desc, xfers, nxfers); + if (ret) { + shell_print(shell, "Failed to private transfer: %d\n", ret); + } + + for (int i = 0; i < nxfers; i++) { + if (xfers[i].rnw) { + shell_hexdump(shell, xfers[i].data.out, xfers[i].len); + } + } + + return ret; +} + +static const char send_ccc_helper[] = "i3c ccc -a -i -w -r "; +static int cmd_send_ccc(const struct shell *shell, size_t argc, char **argv) +{ + const struct device *dev; + struct i3c_ccc_cmd ccc; + struct getopt_state *state; + int c, ret; + + dev = device_get_binding(argv[1]); + if (!dev) { + shell_error(shell, "I3C: Device %s not found.", argv[1]); + return -ENODEV; + } + + ccc.rnw = 0; + ccc.id = 0; + + while ((c = shell_getopt(shell, argc - 1, &argv[1], "ha:i:w:r:")) != -1) { + state = shell_getopt_state_get(shell); + switch (c) { + case 'a': + ccc.addr = strtoul(state->optarg, NULL, 0); + break; + case 'i': + ccc.id = strtoul(state->optarg, NULL, 0); + break; + case 'w': + ccc.rnw = 0; + ccc.payload.data = data_buf[0]; + ccc.payload.length = args_to_wdata(state->optarg, data_buf[0]); + break; + case 'r': + ccc.rnw = 1; + ccc.payload.data = data_buf[0]; + ccc.payload.length = atoi(state->optarg); + break; + case 'h': + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + case '?': + if ((state->optopt == 'a') || (state->optopt == 'w') || + (state->optopt == 'r') || (state->optopt == 'i')) { + shell_print(shell, "Option -%c requires an argument.", + state->optopt); + } else if (isprint(state->optopt)) { + shell_print(shell, "Unknown option `-%c'.", state->optopt); + } else { + shell_print(shell, "Unknown option character `\\x%x'.", + state->optopt); + } + return 1; + default: + break; + } + } + + if (ccc.id == 0) { + shell_print(shell, "CCC ID not assigned\n"); + return SHELL_CMD_HELP_PRINTED; + } + + ccc.ret = 0; + if (ccc.addr != I3C_BROADCAST_ADDR) { + ccc.id |= I3C_CCC_DIRECT; + } + + shell_print(shell, "Send CCC ID 0x%02x (%s) to address 0x%02x\n", ccc.id, + ccc.rnw ? "r" : "w", ccc.addr); + ret = i3c_master_send_ccc(dev, &ccc); + if (ret) { + shell_print(shell, "Failed to send ccc: %d\n", ret); + } + + if (ccc.rnw) { + shell_hexdump(shell, data_buf[0], ccc.payload.length); + } + + return ret; +} + +static const char attach_helper[] = "i3c attach -a -m "; +static int cmd_attach(const struct shell *shell, size_t argc, char **argv) +{ + const struct device *dev; + struct i3c_dev_desc *desc; + struct getopt_state *state; + int c, ret; + + dev = device_get_binding(argv[1]); + if (!dev) { + shell_error(shell, "I3C: Device %s not found.", argv[1]); + return -ENODEV; + } + + desc = &i3c_shell_desc_tbl[i3c_shell_num_of_descs]; + desc->info.i2c_mode = 0; + + while ((c = shell_getopt(shell, argc - 1, &argv[1], "ha:m:")) != -1) { + state = shell_getopt_state_get(shell); + switch (c) { + case 'a': + desc->info.assigned_dynamic_addr = strtoul(state->optarg, NULL, 0); + desc->info.static_addr = desc->info.assigned_dynamic_addr; + break; + case 'm': + desc->info.i2c_mode = strtoul(state->optarg, NULL, 0); + break; + case 'h': + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + case '?': + if ((state->optopt == 'a') || (state->optopt == 'm')) { + shell_print(shell, "Option -%c requires an argument.", + state->optopt); + } else if (isprint(state->optopt)) { + shell_print(shell, "Unknown option `-%c'.", state->optopt); + } else { + shell_print(shell, "Unknown option character `\\x%x'.", + state->optopt); + } + return 1; + default: + break; + } + } + + shell_print(shell, "Attach address 0x%02x to %s", desc->info.assigned_dynamic_addr, + dev->name); + ret = i3c_master_attach_device(dev, desc); + if (ret) { + shell_print(shell, "Failed to attach device: %d", ret); + } else { + i3c_shell_num_of_descs++; + } + + return ret; +} + + +#ifdef CONFIG_I3C_SLAVE_MQUEUE +int i3c_slave_mqueue_read(const struct device *dev, uint8_t *dest, int budget); +int i3c_slave_mqueue_write(const struct device *dev, uint8_t *src, int size); + +static const char smq_xfer_helper[] = "i3c smq -w -r "; +static int cmd_smq_xfer(const struct shell *shell, size_t argc, char **argv) +{ + const struct device *dev; + struct getopt_state *state; + int c, len, ret; + + dev = device_get_binding(argv[1]); + if (!dev) { + shell_error(shell, "I3C: Device %s not found.", argv[1]); + return -ENODEV; + } + + while ((c = shell_getopt(shell, argc - 1, &argv[1], "w:r:h")) != -1) { + state = shell_getopt_state_get(shell); + switch (c) { + case 'w': + len = args_to_wdata(state->optarg, data_buf[0]); + ret = i3c_slave_mqueue_write(dev, data_buf[0], len); + return 0; + case 'r': + len = strtoul(state->optarg, NULL, 0); + i3c_slave_mqueue_read(dev, data_buf[0], len); + shell_hexdump(shell, data_buf[0], len); + return 0; + case 'h': + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + case '?': + if ((state->optopt == 'r') || (state->optopt == 'w')) { + shell_print(shell, "Option -%c requires an argument.", + state->optopt); + } else if (isprint(state->optopt)) { + shell_print(shell, "Unknown option `-%c'.", state->optopt); + } else { + shell_print(shell, "Unknown option character `\\x%x'.", + state->optopt); + } + return 1; + default: + break; + } + } + + return 0; +} +#endif + +#define I3C_SHELL_STACK0_SIZE 1024 +#define I3C_SHELL_STACK1_SIZE 1024 +K_KERNEL_STACK_MEMBER(stack0, I3C_SHELL_STACK0_SIZE); +K_KERNEL_STACK_MEMBER(stack1, I3C_SHELL_STACK1_SIZE); + +k_tid_t tid[2]; +struct k_thread thread[2]; +static const char do_stress_helper[] = "i3c stress -l "; + +static void i3c_stress_target_thread(void *arg0, void *arg1, void *arg2) +{ + const struct device *dev = arg0; + const struct shell *shell = arg1; + int loop_cnt = POINTER_TO_INT(arg2); + + int i, ret; + uint8_t data[16]; + bool do_forever = !loop_cnt; + + shell_print(shell, "I3C target thread start"); + + do { + ret = i3c_slave_mqueue_read(dev, data, 16); + if (ret > 0) { + shell_hexdump(shell, data, 16); + } + k_usleep(10); + + for (i = 0; i < 16; i++) { + data[i] = 16 - i; + } + i3c_slave_mqueue_write(dev, data, 16); + + if (!do_forever && --loop_cnt == 0) { + break; + } + } while (1); + + k_thread_abort(k_current_get()); +} + +static struct i3c_ibi_payload i3c_payload; +uint8_t test_data_rx[256]; +struct i3c_shell_ibi_data { + const struct shell *shell; + struct k_work work; + struct i3c_dev_desc *dev_desc; + struct i3c_ibi_payload payload; +}; + +struct i3c_shell_ibi_data i3c_shell_ibi_user_data; + +static void i3c_shell_ibi_worker(struct k_work *work) +{ + struct i3c_shell_ibi_data *data = CONTAINER_OF(work, struct i3c_shell_ibi_data, work); + struct i3c_priv_xfer xfer; + uint8_t buf[16]; + + /* dump IBI payload data */ + shell_hexdump(data->shell, data->payload.buf, data->payload.size); + + if (IS_MDB_PENDING_READ_NOTIFY(data->payload.buf[0])) { + /* read pending data */ + xfer.data.in = buf; + xfer.len = 16; + xfer.rnw = 1; + i3c_master_priv_xfer(data->dev_desc, &xfer, 1); + shell_hexdump(data->shell, xfer.data.in, xfer.len); + } +} + +static struct i3c_ibi_payload *test_ibi_write_requested(struct i3c_dev_desc *desc) +{ + i3c_payload.buf = test_data_rx; + i3c_payload.size = 0; + i3c_payload.max_payload_size = 16; + + return &i3c_payload; +} + +static void test_ibi_write_done(struct i3c_dev_desc *desc) +{ + memcpy(&i3c_shell_ibi_user_data.payload, &i3c_payload, sizeof(struct i3c_ibi_payload)); + k_work_submit(&i3c_shell_ibi_user_data.work); +} + +static struct i3c_ibi_callbacks i3c_ibi_def_callbacks = { + .write_requested = test_ibi_write_requested, + .write_done = test_ibi_write_done, +}; + +static void i3c_stress_daa_thread(void *arg0, void *arg1, void *arg2) +{ + const struct device *dev = arg0; + const struct shell *shell = arg1; + int loop_cnt = POINTER_TO_INT(arg2); + + bool do_forever = !loop_cnt; + + shell_print(shell, "I3C DAA thread start"); + do { + i3c_master_send_aasa(dev); + k_msleep(1000); + + if (!do_forever && --loop_cnt == 0) { + break; + } + } while (1); + + k_thread_abort(k_current_get()); +} + +static void i3c_stress_main_thread(void *arg0, void *arg1, void *arg2) +{ + const struct device *dev = arg0; + const struct shell *shell = arg1; + int loop_cnt = POINTER_TO_INT(arg2); + + struct i3c_priv_xfer xfer; + struct i3c_dev_desc *desc = &i3c_shell_desc_tbl[0]; + uint8_t data[16]; + int i; + bool do_forever = !loop_cnt; + + if (!i3c_shell_ibi_user_data.work.handler) { + k_work_init(&i3c_shell_ibi_user_data.work, i3c_shell_ibi_worker); + } + i3c_shell_ibi_user_data.shell = shell; + i3c_shell_ibi_user_data.dev_desc = desc; + + /* register dev_desc */ + desc->info.static_addr = 0x9; + desc->info.assigned_dynamic_addr = 0x9; + desc->info.i2c_mode = 0; + desc->info.pid = 0x7ec80011000; + desc->info.bcr = 0x66; + desc->info.dcr = 0; + i3c_master_attach_device(dev, desc); + + /* Assign dynamic address through SETAASA */ + i3c_master_send_aasa(dev); + i3c_master_request_ibi(desc, &i3c_ibi_def_callbacks); + i3c_master_enable_ibi(desc); + + tid[1] = k_thread_create(&thread[1], stack1, I3C_SHELL_STACK1_SIZE, + (k_thread_entry_t)i3c_stress_daa_thread, (void *)dev, + (void *)shell, INT_TO_POINTER(loop_cnt), 55, 0, K_FOREVER); + + k_thread_name_set(tid[1], "i3c_stress_daa"); + k_thread_start(tid[1]); + + /* init private write data */ + for (i = 0; i < 16; i++) { + data[i] = i; + } + xfer.data.out = data; + xfer.len = 16; + xfer.rnw = 0; + + shell_print(shell, "I3C main thread start"); + do { + i3c_master_priv_xfer(desc, &xfer, 1); + k_msleep(1000); + + if (!do_forever && --loop_cnt == 0) { + break; + } + } while (1); + + k_thread_abort(k_current_get()); +} + +static int cmd_do_stress(const struct shell *shell, size_t argc, char **argv) +{ + const struct device *dev; + struct getopt_state *state; + int c, target_mode = 0, loop_cnt = 0; + + dev = device_get_binding(argv[1]); + if (!dev) { + shell_error(shell, "I3C: Device %s not found.", argv[1]); + return -ENODEV; + } + + if (strstr(dev->name, "SMQ") != NULL) { + /* + * if "SMQ" is present in device name, implies the I3C controller operates in + * the target mode + */ + target_mode = 1; + } + + while ((c = shell_getopt(shell, argc - 1, &argv[1], "l:")) != -1) { + state = shell_getopt_state_get(shell); + switch (c) { + case 'l': + loop_cnt = strtoul(state->optarg, NULL, 0); + return 0; + case 'h': + shell_help(shell); + return SHELL_CMD_HELP_PRINTED; + case '?': + if (state->optopt == 'l') { + shell_print(shell, "Option -%c requires an argument.", + state->optopt); + } else if (isprint(state->optopt)) { + shell_print(shell, "Unknown option `-%c'.", state->optopt); + } else { + shell_print(shell, "Unknown option character `\\x%x'.", + state->optopt); + } + return 1; + default: + break; + } + } + + if (strcmp(k_thread_state_str(&thread[0]), "") == 0 || + strcmp(k_thread_state_str(&thread[0]), "dead") == 0) { + if (target_mode) { + tid[0] = k_thread_create(&thread[0], stack0, I3C_SHELL_STACK0_SIZE, + (k_thread_entry_t)i3c_stress_target_thread, + (void *)dev, (void *)shell, + INT_TO_POINTER(loop_cnt), 55, 0, K_FOREVER); + } else { + tid[0] = k_thread_create(&thread[0], stack0, I3C_SHELL_STACK0_SIZE, + (k_thread_entry_t)i3c_stress_main_thread, + (void *)dev, (void *)shell, + INT_TO_POINTER(loop_cnt), 55, 0, K_FOREVER); + } + + if (!tid[0]) { + shell_print(shell, "thread creat failed = %d", tid[0]); + return 1; + } + + if (target_mode) { + k_thread_name_set(tid[0], "i3c_stress_target"); + } else { + k_thread_name_set(tid[0], "i3c_stress_main"); + } + k_thread_start(tid[0]); + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_i3c_cmds, + SHELL_CMD(attach, &dsub_device_name, attach_helper, cmd_attach), + SHELL_CMD(ccc, &dsub_device_name, send_ccc_helper, cmd_send_ccc), + SHELL_CMD(xfer, &dsub_device_name, priv_xfer_helper, cmd_priv_xfer), +#ifdef CONFIG_I3C_SLAVE_MQUEUE + SHELL_CMD(smq, &dsub_device_name, smq_xfer_helper, cmd_smq_xfer), + SHELL_CMD(stress, &dsub_device_name, do_stress_helper, cmd_do_stress), +#endif + SHELL_SUBCMD_SET_END); +SHELL_CMD_REGISTER(i3c, &sub_i3c_cmds, "I3C commands", NULL); diff --git a/drivers/i3c/slave/CMakeLists.txt b/drivers/i3c/slave/CMakeLists.txt new file mode 100644 index 00000000000000..9771559240d91b --- /dev/null +++ b/drivers/i3c/slave/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (c) 2021 ASPEED Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources_ifdef(CONFIG_I3C_SLAVE_MQUEUE i3c_slave_mqueue.c) diff --git a/drivers/i3c/slave/Kconfig b/drivers/i3c/slave/Kconfig new file mode 100644 index 00000000000000..7d37ab34f4d0e8 --- /dev/null +++ b/drivers/i3c/slave/Kconfig @@ -0,0 +1,24 @@ +# I3C Slave configuration options + +# Copyright (c) 2021 ASPEED Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +# +# I3C slave options +# +menuconfig I3C_SLAVE + bool "I3C Slave Drivers" + help + Enable I3C Slave Driver Configuration + +if I3C_SLAVE + +config I3C_SLAVE_INIT_PRIORITY + int "Init priority" + default 60 + help + I3C Slave device driver initialization priority. + + source "drivers/i3c/slave/Kconfig.i3c_slave_mqueue" + +endif # I3C_SLAVE diff --git a/drivers/i3c/slave/Kconfig.i3c_slave_mqueue b/drivers/i3c/slave/Kconfig.i3c_slave_mqueue new file mode 100644 index 00000000000000..bf5ecf32230e4f --- /dev/null +++ b/drivers/i3c/slave/Kconfig.i3c_slave_mqueue @@ -0,0 +1,10 @@ +# I3C Slave mqueue configuration options + +# Copyright (c) 2021 Aspeed Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +config I3C_SLAVE_MQUEUE + bool "I3C Slave MQueue driver" + default y + help + Enable I3C slave mqueue driver diff --git a/drivers/i3c/slave/i3c_slave_mqueue.c b/drivers/i3c/slave/i3c_slave_mqueue.c new file mode 100644 index 00000000000000..8c6f2599d5b4e1 --- /dev/null +++ b/drivers/i3c/slave/i3c_slave_mqueue.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT i3c_slave_mqueue + +#include +#include +#include +#include +#include +#include +#include +#define LOG_LEVEL CONFIG_I3C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(i3c_slave_mqueue); + +struct i3c_slave_mqueue_config { + char *controller_name; + int msg_size; + int num_of_msgs; + int mdb; +}; + +struct i3c_slave_mqueue_obj { + const struct device *i3c_controller; + struct i3c_slave_payload *msg_curr; + struct i3c_slave_payload *msg_queue; + int in; + int out; +}; + +#define DEV_CFG(dev) ((struct i3c_slave_mqueue_config *)(dev)->config) +#define DEV_DATA(dev) ((struct i3c_slave_mqueue_obj *)(dev)->data) + +static struct i3c_slave_payload *i3c_slave_mqueue_write_requested(const struct device *dev) +{ + struct i3c_slave_mqueue_obj *obj = DEV_DATA(dev); + + return obj->msg_curr; +} + +static void i3c_slave_mqueue_write_done(const struct device *dev) +{ + struct i3c_slave_mqueue_obj *obj = DEV_DATA(dev); + struct i3c_slave_mqueue_config *config = DEV_CFG(dev); + + /* update pointer */ + obj->in = (obj->in + 1) & (config->num_of_msgs - 1); + obj->msg_curr = &obj->msg_queue[obj->in]; + + /* if queue full, skip the oldest un-read message */ + if (obj->in == obj->out) { + LOG_WRN("buffer overflow\n"); + obj->out = (obj->out + 1) & (config->num_of_msgs - 1); + } +} + +static const struct i3c_slave_callbacks i3c_slave_mqueue_callbacks = { + .write_requested = i3c_slave_mqueue_write_requested, + .write_done = i3c_slave_mqueue_write_done, +}; + +/** + * @brief application reads the data from the message queue + * + * @param dev i3c slave mqueue device + * @return int -1: message queue empty + */ +int i3c_slave_mqueue_read(const struct device *dev, uint8_t *dest, int budget) +{ + struct i3c_slave_mqueue_config *config = DEV_CFG(dev); + struct i3c_slave_mqueue_obj *obj = DEV_DATA(dev); + struct i3c_slave_payload *msg; + int ret; + + if (obj->out == obj->in) { + return 0; + } + + msg = &obj->msg_queue[obj->out]; + ret = (msg->size > budget) ? budget : msg->size; + memcpy(dest, msg->buf, ret); + + obj->out = (obj->out + 1) & (config->num_of_msgs - 1); + + return ret; +} + +int i3c_slave_mqueue_write(const struct device *dev, uint8_t *src, int size) +{ + struct i3c_slave_mqueue_config *config = DEV_CFG(dev); + struct i3c_slave_mqueue_obj *obj = DEV_DATA(dev); + struct i3c_ibi_payload ibi; + uint32_t event_en; + int ret; + uint8_t dynamic_addr; + + ret = i3c_slave_get_dynamic_addr(obj->i3c_controller, &dynamic_addr); + if (ret) { + return -ENOTCONN; + } + + ret = i3c_slave_get_event_enabling(obj->i3c_controller, &event_en); + if (ret || !(event_en & I3C_SLAVE_EVENT_SIR)) { + return -EACCES; + } + + struct i3c_slave_payload read_data; + + read_data.buf = src; + read_data.size = size; + + if (IS_MDB_PENDING_READ_NOTIFY(config->mdb)) { + /* response with ibi */ + uint32_t ibi_data = config->mdb; + + ibi.buf = (uint8_t *)&ibi_data; + ibi.size = 1; + return i3c_slave_put_read_data(obj->i3c_controller, &read_data, &ibi); + } + + /* response without ibi, master should support retry */ + return i3c_slave_put_read_data(obj->i3c_controller, &read_data, NULL); +} + +static void i3c_slave_mqueue_init(const struct device *dev) +{ + struct i3c_slave_mqueue_config *config = DEV_CFG(dev); + struct i3c_slave_mqueue_obj *obj = DEV_DATA(dev); + struct i3c_slave_setup slave_data; + uint8_t *buf; + int i; + + LOG_DBG("msg size %d, n %d\n", config->msg_size, config->num_of_msgs); + LOG_DBG("bus name : %s\n", config->controller_name); + __ASSERT((config->num_of_msgs & (config->num_of_msgs - 1)) == 0, + "number of msgs must be power of 2\n"); + + obj->i3c_controller = device_get_binding(config->controller_name); + + buf = k_calloc(config->msg_size, config->num_of_msgs); + if (!buf) { + LOG_ERR("failed to create message buffer\n"); + return; + } + + obj->msg_queue = (struct i3c_slave_payload *)k_malloc(sizeof(struct i3c_slave_payload) * + config->num_of_msgs); + if (!obj->msg_queue) { + LOG_ERR("failed to create message queue\n"); + return; + } + + for (i = 0; i < config->num_of_msgs; i++) { + obj->msg_queue[i].buf = buf + (i * config->msg_size); + obj->msg_queue[i].size = 0; + } + + obj->in = 0; + obj->out = 0; + obj->msg_curr = &obj->msg_queue[0]; + + slave_data.max_payload_len = config->msg_size; + slave_data.callbacks = &i3c_slave_mqueue_callbacks; + slave_data.dev = dev; + i3c_slave_register(obj->i3c_controller, &slave_data); +} + +BUILD_ASSERT(CONFIG_I3C_SLAVE_INIT_PRIORITY > CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + "I3C controller must be initialized prior to target device initialization"); + +#define I3C_SLAVE_MQUEUE_INIT(n) \ + static int i3c_slave_mqueue_config_func_##n(const struct device *dev); \ + static const struct i3c_slave_mqueue_config i3c_slave_mqueue_config_##n = { \ + .controller_name = DT_INST_BUS_LABEL(n), \ + .msg_size = DT_INST_PROP(n, msg_size), \ + .num_of_msgs = DT_INST_PROP(n, num_of_msgs), \ + .mdb = DT_INST_PROP(n, mandatory_data_byte), \ + }; \ + \ + static struct i3c_slave_mqueue_obj i3c_slave_mqueue_obj##n; \ + \ + DEVICE_DT_INST_DEFINE(n, &i3c_slave_mqueue_config_func_##n, NULL, \ + &i3c_slave_mqueue_obj##n, &i3c_slave_mqueue_config_##n, POST_KERNEL, \ + CONFIG_I3C_SLAVE_INIT_PRIORITY, NULL); \ + \ + static int i3c_slave_mqueue_config_func_##n(const struct device *dev) \ + { \ + i3c_slave_mqueue_init(dev); \ + return 0; \ + } + +DT_INST_FOREACH_STATUS_OKAY(I3C_SLAVE_MQUEUE_INIT) diff --git a/dts/arm/nuvoton/npcm400f.dtsi b/dts/arm/nuvoton/npcm400f.dtsi index 972b620b31a608..257c07907a80f3 100644 --- a/dts/arm/nuvoton/npcm400f.dtsi +++ b/dts/arm/nuvoton/npcm400f.dtsi @@ -161,18 +161,22 @@ i3c0: i3c0@40004000 { compatible = "nuvoton,npcm4xx-i3c"; - instance-id = <0>; + #address-cells = <1>; + #size-cells = <0>; reg = <0x40004000 0x400>; interrupts = <20 3>; + instance-id = <0>; status = "disabled"; label = "I3C_0"; }; i3c1: i3c1@40004400 { compatible = "nuvoton,npcm4xx-i3c"; - instance-id = <1>; + #address-cells = <1>; + #size-cells = <0>; reg = <0x40004400 0x400>; interrupts = <1 3>; + instance-id = <1>; status = "disabled"; label = "I3C_1"; }; @@ -188,6 +192,18 @@ label = "USBD_0"; status = "disabled"; }; + + usbd0: usbd@4001b000 { + compatible = "nuvoton,npcm4xx-usbd"; + reg = <0x4001b000 0x1000>; + interrupts = <13 3>; + num-bidir-endpoints = <13>; + usbd-ram-size = <4096>; + pinctrl-0 = <&pinctrl_usbd_phy_iclk>; + /* pinctrl-0 = <&pinctrl_usbd_phy_xclk>; */ + label = "USBD_0"; + status = "disabled"; + }; }; soc-id { diff --git a/dts/bindings/i3c/i3c-controller.yaml b/dts/bindings/i3c/i3c-controller.yaml index cfba1e09e3d666..dbbc63ea4fd59d 100644 --- a/dts/bindings/i3c/i3c-controller.yaml +++ b/dts/bindings/i3c/i3c-controller.yaml @@ -12,7 +12,8 @@ properties: required: false type: int default: 1000000 - description: I2C mode and Open-drain mode SCL frequency in HZ + description: I2C mode SCL frequency in HZ + i3c-scl-hz: required: false type: int diff --git a/dts/bindings/i3c/i3c-slave-mqueue.yaml b/dts/bindings/i3c/i3c-slave-mqueue.yaml new file mode 100644 index 00000000000000..481bf692fd55d2 --- /dev/null +++ b/dts/bindings/i3c/i3c-slave-mqueue.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +description: NUVOTON I3C MQueue device + +compatible: "i3c-slave-mqueue" + +include: base.yaml + +on-bus: i3c + +properties: + msg-size: + type: int + required: true + description: | + the size of the single message in byte, used to specify max read/write length + + num-of-msgs: + type: int + required: true + description: | + number of the messages + + mandatory-data-byte: + type: int + required: true + description: | + mandatory data byte (MDB), used to specify how slave provide response data diff --git a/dts/bindings/i3c/nuvoton,npcm4xx-i3c.yaml b/dts/bindings/i3c/nuvoton,npcm4xx-i3c.yaml index 6fd310d43b5ccf..7e66d622ba1cf0 100644 --- a/dts/bindings/i3c/nuvoton,npcm4xx-i3c.yaml +++ b/dts/bindings/i3c/nuvoton,npcm4xx-i3c.yaml @@ -8,77 +8,74 @@ compatible: "nuvoton,npcm4xx-i3c" include: [i3c-controller.yaml, nuvoton-pinctrl.yaml] properties: -# specify port number instance-id: - required: true - type: int - description: Instance ID of the device - -#specify port address (master: dynamic address, slave: assigned address for ENTDAA or static address for SETAASA and SETDASA) - assigned-address: required: true type: int description: | - Dynamic address when playing the role as the main master. Static address when playing the role as the slave. - If extra-gpios exist, the lsb of slave static address will be replaced by the value of extra-gpios. + Instance ID of the device, used to specify port number -# not declare --> master -# declare --> slave slave: required: false type: boolean - description: Initialized as slave / master + description: Initialized as slave (default) / master -# declare --> secondary master if slave is config secondary: required: false type: boolean description: Initialized as a secondary master - manufacture-id: - required: false + assigned-address: + required: true type: int - description: PID[47:32] + description: | + The default address of the device part-id: required: false type: int - description: PID[31:16] + description: | + PID[31:16], used for bus enumeration with entdaa vendor-def-id: required: false type: int - description: PID[11:0] + description: | + PID[11:0], used for bus enumeration with entdaa bcr: required: false type: int - description: Bus Characteristics Register + description: | + Bus Characteristics Register, used for bus enumeration with entdaa dcr: required: false type: int - description: Device Characteristics Register + description: | + Device Characteristics Register, used for bus enumeration with entdaa busno: - required: false + required: true type: int - description: Bus Number + description: | + Bus Number, used for bus enumeration -# pinctrl pinctrl-0: + type: phandles required: true + description: configurations of pinmux controllers - extra-gpios: - type: phandle-array - required: false - description: | - Used to add the extra info to identify different BIC by GPIOs. +# extra-gpios: +# type: phandle-array +# required: false +# description: | +# Used to add the extra info to identify different BIC by GPIOs. ibi-append-pec: required: false type: boolean - description: Append PEC byte to the IBI data. Enable this option in slave mode if the master device is AST2600 or AST1030A0. + description: | + Append PEC byte to the IBI data. priv-xfer-pec: required: false @@ -88,14 +85,9 @@ properties: The PEC will auto append to the tail of the data when doing private transfer and verify the PEC when receiving the data from master. - pid-extra-info: - required: false - type: int - description: | - Extra information of the PID Bits[11:0]. Use to identify the different BIC. - If extra-gpios exist, the extra pid will be replaced by the value of extra-gpios. - - sda-tx-hold-ns: - required: false - type: int - description: The hold time of the SDA with respect to the SCL edge. The unit is in nanosecond. +# pid-extra-info: +# required: false +# type: int +# description: | +# Extra information of the PID Bits[11:0]. Use to identify the different BIC. +# If extra-gpios exist, the extra pid will be replaced by the value of extra-gpios. diff --git a/include/drivers/i3c/NPCM4XX/api_i3c.h b/include/drivers/i3c/NPCM4XX/api_i3c.h index b49113fc65d99d..049cb9acd39e15 100644 --- a/include/drivers/i3c/NPCM4XX/api_i3c.h +++ b/include/drivers/i3c/NPCM4XX/api_i3c.h @@ -38,6 +38,7 @@ extern __u8 I3C_Task_Engine(void); extern void api_I3C_Master_Start_Request(__u32 Parm); extern void api_I3C_Master_Stop(__u32 Parm); +extern void api_I3C_Master_Retry(__u32 Parm); extern void api_I3C_Master_Run_Next_Frame(__u32 Parm); extern void api_I3C_Master_New_Request(__u32 Parm); diff --git a/include/drivers/i3c/NPCM4XX/hal_I3C.h b/include/drivers/i3c/NPCM4XX/hal_I3C.h index 8b46abd99a0baf..7f270a6bb15b0e 100644 --- a/include/drivers/i3c/NPCM4XX/hal_I3C.h +++ b/include/drivers/i3c/NPCM4XX/hal_I3C.h @@ -82,6 +82,12 @@ extern I3C_ErrCode_Enum hal_I3C_disable_interface(I3C_PORT_Enum Port); extern I3C_ErrCode_Enum hal_I3C_enable_interrupt(I3C_PORT_Enum Port); extern I3C_ErrCode_Enum hal_I3C_disable_interrupt(I3C_PORT_Enum Port); +extern __u32 hal_I3C_get_MAXRD(I3C_PORT_Enum Port); +extern void hal_I3C_set_MAXRD(I3C_PORT_Enum Port, __u32 val); + +extern __u32 hal_I3C_get_MAXWR(I3C_PORT_Enum Port); +extern void hal_I3C_set_MAXWR(I3C_PORT_Enum Port, __u32 val); + extern __u32 hal_I3C_get_mstatus(I3C_PORT_Enum Port); extern void hal_I3C_set_mstatus(I3C_PORT_Enum Port, __u32 val); extern I3C_IBITYPE_Enum hal_I3C_get_ibiType(I3C_PORT_Enum Port); diff --git a/include/drivers/i3c/NPCM4XX/i3c_drv.h b/include/drivers/i3c/NPCM4XX/i3c_drv.h index ada3e107938a45..7d5b8c9d4282a2 100644 --- a/include/drivers/i3c/NPCM4XX/i3c_drv.h +++ b/include/drivers/i3c/NPCM4XX/i3c_drv.h @@ -95,6 +95,10 @@ struct i3c_npcm4xx_config { bool secondary; uint32_t i3c_scl_hz; uint32_t i2c_scl_hz; + /* uint16_t manufacture-id; PID[5:4] */ + uint16_t part_id; /* PID[3:2] */ + uint16_t vendor_def_id; /* PID[1:0] */ + uint16_t pid_extra_info; int ibi_append_pec; int priv_xfer_pec; @@ -130,7 +134,12 @@ struct i3c_npcm4xx_obj { struct i3c_dev_desc *dev_descs[DEVICE_COUNT_MAX]; /* slave mode data */ + struct i3c_slave_payload *msg_curr; + struct i3c_slave_payload *msg_queue; + int in; /* msg in id */ + int out; /* msg end id */ struct i3c_slave_setup slave_data; + osEventFlagsId_t ibi_event; osEventFlagsId_t data_event; uint16_t extra_val; diff --git a/include/drivers/i3c/NPCM4XX/i3c_master.h b/include/drivers/i3c/NPCM4XX/i3c_master.h index 883736b4fbd848..6719fa8d8ea19c 100644 --- a/include/drivers/i3c/NPCM4XX/i3c_master.h +++ b/include/drivers/i3c/NPCM4XX/i3c_master.h @@ -35,6 +35,7 @@ extern __u32 I3C_DO_SETNEWDA(I3C_TASK_INFO_t *pTaskInfo); /* Operation */ extern void I3C_Master_Start_Request(__u32 Parm); extern void I3C_Master_Stop_Request(__u32 Parm); +extern void I3C_Master_Retry_Frame(__u32 Parm); extern void I3C_Master_End_Request(__u32 Parm); extern void I3C_Master_Run_Next_Frame(__u32 Parm); extern void I3C_Master_New_Request(__u32 Parm); diff --git a/include/drivers/i3c/NPCM4XX/i3c_slave.h b/include/drivers/i3c/NPCM4XX/i3c_slave.h index 9dd7950da7c966..46172cb9d1efe6 100644 --- a/include/drivers/i3c/NPCM4XX/i3c_slave.h +++ b/include/drivers/i3c/NPCM4XX/i3c_slave.h @@ -33,7 +33,7 @@ extern I3C_ErrCode_Enum Setup_Slave_Write_DMA(I3C_DEVICE_INFO_t *pDevice); extern I3C_ErrCode_Enum Setup_Slave_Read_DMA(I3C_DEVICE_INFO_t *pDevice); extern I3C_ErrCode_Enum Setup_Slave_IBI_DMA(I3C_DEVICE_INFO_t *pDevice); -extern void I3C_Update_Dynamic_Address(__u32 Parm); +extern uint8_t I3C_Update_Dynamic_Address(__u32 Parm); extern void I3C_Prepare_To_Read_Command(__u32 Parm); extern uint8_t GetCmdWidth(uint8_t width_type); diff --git a/include/drivers/i3c/NPCM4XX/pub_I3C.h b/include/drivers/i3c/NPCM4XX/pub_I3C.h index e6139e47a94a6a..b7909c76ba4c7c 100644 --- a/include/drivers/i3c/NPCM4XX/pub_I3C.h +++ b/include/drivers/i3c/NPCM4XX/pub_I3C.h @@ -14,6 +14,8 @@ #include #include +#define WAIT_SLAVE_PREPARE_RESPONSE_TIME 10 /* unit: us */ + /* generic data type used in lib source for compatibility */ typedef uint8_t __u8; typedef uint16_t __u16; @@ -70,7 +72,7 @@ typedef int32_t __s32; #define I3C_TRANSFER_SPEED_UNDEF 0 -#define I3C_PAYLOAD_SIZE_MAX 69 +#define I3C_PAYLOAD_SIZE_MAX 256 #define IBI_PAYLOAD_SIZE_MAX 8 enum I3C_PORT { @@ -96,6 +98,8 @@ enum I3C_PORT { /*#define I3C_BROADCAST_ADDR 0x7E*/ +#define RX_HANDLER_MAX 3 + enum I3C_DEVICE_TYPE { I3C_DEVICE_TYPE_PURE_I3C = 0U, I3C_DEVICE_TYPE_PURE_I2C = 1U, @@ -240,10 +244,10 @@ struct I3C_DEVICE_INFO_SHORT; #define cmd_t union cmd_t /*typedef */struct cmd_attrib { - __u8 endian : 1; /* 0b: little, 1b: bigh endian, if width != 0 */ - __u8 width : 1; /* 0b = 1, 1b = 2 */ - __u8 write : 1; - __u8 read : 1; + __u8 endian : 1; /* 0b: little, 1b: bigh endian, if width != 0 */ + __u8 width : 1; /* 0b = 1, 1b = 2 */ + __u8 write : 1; /* wrtiable */ + __u8 read : 1; /* readable */ } /* cmd_attrib_t */; #define cmd_attrib_t struct cmd_attrib @@ -518,6 +522,9 @@ struct I3C_DEVICE_INFO { __u16 vendorID; /* Device vendor ID (manufacture ID).*/ __u32 partNumber; /* Device part number info */ + __u32 max_rd_len; + __u32 max_wr_len; + /* master config */ _Bool enableOpenDrainHigh; _Bool enableOpenDrainStop; @@ -541,6 +548,7 @@ struct I3C_DEVICE_INFO { /* 0b: slave should reset command, RX DMA/FIFO */ __u8 cmdIndex; I3C_REG_ITEM_t *pReg; + uint8_t regCnt; _Bool bAbort; volatile __u8 task_count; @@ -672,6 +680,229 @@ struct LSM6DSO_DEVICE_INFO { __u8 temp_val; }; +#define SPD5118_POST_INIT_STATE_Enum enum SPD5118_POST_INIT_STATE + +enum SPD5118_POST_INIT_STATE { + SPD5118_POST_INIT_STATE_Default, + + SPD5118_POST_INIT_STATE_Sync_Wait, + SPD5118_POST_INIT_STATE_Sync_Done, + + SPD5118_POST_INIT_STATE_1_Wait, + SPD5118_POST_INIT_STATE_1_Done, + + SPD5118_POST_INIT_STATE_2, + SPD5118_POST_INIT_STATE_2_Wait, + SPD5118_POST_INIT_STATE_2_Done, + + SPD5118_POST_INIT_STATE_3, + SPD5118_POST_INIT_STATE_3_Wait, + SPD5118_POST_INIT_STATE_3_Done, + + SPD5118_POST_INIT_STATE_4, + SPD5118_POST_INIT_STATE_4_Wait, + SPD5118_POST_INIT_STATE_4_Done, + + SPD5118_POST_INIT_STATE_5, + SPD5118_POST_INIT_STATE_5_Wait, + SPD5118_POST_INIT_STATE_5_Done, + + SPD5118_POST_INIT_STATE_6, + SPD5118_POST_INIT_STATE_6_Wait, + SPD5118_POST_INIT_STATE_6_Done, + + SPD5118_POST_INIT_STATE_7, + SPD5118_POST_INIT_STATE_7_Wait, + SPD5118_POST_INIT_STATE_7_Done, + + SPD5118_POST_INIT_STATE_8, + SPD5118_POST_INIT_STATE_8_Wait, + SPD5118_POST_INIT_STATE_8_Done, + + SPD5118_POST_INIT_STATE_9, + SPD5118_POST_INIT_STATE_9_Wait, + SPD5118_POST_INIT_STATE_9_Done, + + SPD5118_POST_INIT_STATE_10, + SPD5118_POST_INIT_STATE_10_Wait, + SPD5118_POST_INIT_STATE_10_Done, + + SPD5118_POST_INIT_STATE_11, + SPD5118_POST_INIT_STATE_11_Wait, + SPD5118_POST_INIT_STATE_11_Done, + + SPD5118_POST_INIT_STATE_12, + SPD5118_POST_INIT_STATE_12_Wait, + SPD5118_POST_INIT_STATE_12_Done, + + SPD5118_POST_INIT_STATE_13, + SPD5118_POST_INIT_STATE_13_Wait, + SPD5118_POST_INIT_STATE_13_Done, + + SPD5118_POST_INIT_STATE_END, +}; + + #define SPD5118_STATE_Enum enum SPD5118_STATE + +enum SPD5118_STATE { + SPD5118_STATE_DEFAULT, + SPD5118_STATE_RESET, + SPD5118_STATE_CLEAR, + SPD5118_STATE_IDLE, + SPD5118_STATE_TASK_CANNOT_COMPLETE, + SPD5118_STATE_WAIT_WR_OP, + SPD5118_STATE_WAIT_NEXT_WR_OP, + SPD5118_STATE_WAIT_WR_OP_END, + SPD5118_STATE_GET_MR11, + SPD5118_STATE_GET_MR11_END, + SPD5118_STATE_WRITE_REG, + SPD5118_STATE_WRITE_REG_END, + SPD5118_STATE_WRITE_REG_NEXT, + SPD5118_STATE_READ_REG, + SPD5118_STATE_READ_REG_FAIL_NOT_PRESENT, + SPD5118_STATE_READ_REG_FAIL_FORMAT, + SPD5118_STATE_READ_REG_END, + SPD5118_STATE_READ_REG_NEXT, + SPD5118_STATE_GOTO_PAGE, + SPD5118_STATE_GOTO_PAGE_CHECK, + SPD5118_STATE_CHECK_PAGE, + SPD5118_STATE_GOTO_PAGE_END, + SPD5118_STATE_WRITE_EEPROM, + SPD5118_STATE_READ_EEPROM, + SPD5118_STATE_WRITE_ABORT_EEPROM, /* error case */ + + SPD5118_STATE_DEFAULT_READ, +}; + +#define SPD5118_OP_Enum enum SPD5118_OP + +enum SPD5118_OP { + SPD5118_OP_PEC_SYNC, + SPD5118_OP_REG_WRITE, + SPD5118_OP_REG_READ, + SPD5118_OP_REG_DEFAULT_POINTER_READ, + SPD5118_OP_EEPROM_WRITE, + SPD5118_OP_EEPROM_READ, +}; + +#define SPD5118_TASK_t struct SPD5118_TASK + +struct SPD5118_TASK { + /* used to link the next SPD5118 task if the current task is not finished */ + struct SPD5118_TASK *pNextTask; + + /* used to store task parameters */ + __u8 address; + SPD5118_OP_Enum op; + _Bool bPEC; + _Bool b2B; + _Bool bRunI3C; /* optional, used to switch i2c and i3c*/ + __u16 offset; + __u8 *pWrBuf, *pRdBuf; + __u16 *pWrLen, *pRdLen; + + __u16 retry_cnt; /* used to retry */ + __u16 timeout; /* used to validate task timeout */ + __u16 access_offset; /* used to record status for long read/write */ + __u8 access_size; /* used to validate read length */ +}; + +#define SPD5118_DEVICE_INFO_t struct SPD5118_DEVICE_INFO + +struct SPD5118_DEVICE_INFO { + I3C_DEVICE_INFO_t i3c_device; + __u32 baudrate; + + __u8 bPEC : 1; + + SPD5118_POST_INIT_STATE_Enum post_init_state; + SPD5118_STATE_Enum state; + __u8 checkMask; + __u16 reset_timeout; + + __u8 MR0; /* Device Type, MSB */ + __u8 MR1; /* Device Type, LSB */ + __u8 MR2; /* Revision */ + __u8 MR3; /* Vendor ID, Byte 0 */ + __u8 MR4; /* Vendor ID, Byte 1 */ + __u8 MR5; /* Device Capability */ + /* [1] Internal Temperature Sensor Support */ + /* [0] Hub function support */ + __u8 MR6; /* Device Write Recovery Time Capability */ + /* [7:4]: 0, .., 10, 50, 100, 200, 500 */ + /* [1:0] == 00b, ns */ + /* == 01b, us */ + /* == 10b, ms */ + /* == 11b, Reserved */ + __u8 MR11; /* I2C Legacy Mode Device Configuration*/ + /* [3] == 0b, 1 Byte addressing */ + /* == 1b, 2 Byte addressing */ + /* [2:0], Non Volatile Memory Address Page Pointer in I2C Legacy Mode */ + __u8 MR12; /* Write Protection For NVM Blocks [7:0] */ + __u8 MR13; /* Write Protection for NVM Blocks [15:8] */ + __u8 MR14; /* Device Configuration - Host and Local Interface IO; */ + /* [5] Local Interface Pull Up Resistor Configuration */ + __u8 MR18; /* Device Configuration */ + /* [7] PEC, I3C mode only */ + /* [6] T bit Disable, I3C mode only */ + /* [5] Interface Selection */ + /* [4] Default Read Address Pointer Enable */ + /* [3:2], Default Read Pointer Starting Address */ + /* [1], Burst Length for Default Read Pointer Address for PEC Calculation */ + __u8 MR19; /* Clear Register MR51 Temperature Status Command */ + /* [3] CRIT_LOW, clear MR51[3] */ + /* [2] CRIT_HIGH, clear MR51[2] */ + /* [1] LOW, clear MR51[1] */ + /* [0] HIGH, clear MR51[0] */ + __u8 MR20; /* Clear Register MR52 Error Status Command */ + /* [7] Write or Read Attempt while SPD Device Busy Error, Clear MR52[7] */ + /* [6] Write Attempt to Protected NVM Block Error, Clear MR52[6] */ + /* [5] Write Attempt to NVM Protection Register Error, Clear MR52[5] */ + /* [1] PEC Error, Clear MR52[1] */ + /* [0] Parity Error, Clear MR52[0] */ + __u8 MR26; /* TS Configuration */ + /* [0] Disable thermal sensor */ + __u8 MR27; /* Interrupt Configurations */ + /* [7] Clear MR48[8], MR51[3:0], MR52[7:5,3,1:0] */ + /* [4] IBI enabled for MR52[7:5, 1:0] */ + /* [3] MR27[4] = 1 & MR27[3] = 1 & MR51[3] = 1 generate IBI for CRIT_LOW */ + /* [2] MR27[4] = 1 & MR27[2] = 1 & MR51[2] = 1 generate IBI for CRIT_HIGH */ + /* [1] MR27[4] = 1 & MR27[1] = 1 & MR51[1] = 1 generate IBI for LOW */ + /* [0] MR27[4] = 1 & MR27[0] = 1 & MR51[0] = 1 generate IBI for HIGH */ + + __u8 MR28; /* TS Temperature High Limit Configuration - Low Byte */ + __u8 MR29; /* TS Temperature High Limit Configuration - High Byte */ + __u8 MR30; /* TS Temperature Low Limit Configuration - Low Byte */ + __u8 MR31; /* TS Temperature Low Limit Configuration - High Byte */ + __u8 MR32; /* TS Critical Temperature High Limit Configuration - Low Byte */ + __u8 MR33; /* TS Critical Temperature High Limit Configuration - High Byte */ + __u8 MR34; /* TS Critical Temperature Low Limit Configuration - Low Byte */ + __u8 MR35; /* TS Critical Temperature Low Limit Configuration - High Byte */ + + __u8 MR48; /* Device Status */ + /* [7] Pending IBI */ + /* [3] Write Operation Status */ + /* [2] Write Protect Override Status */ + __u8 MR49; /* TS Current Sensed Temperature - Low Byte */ + __u8 MR50; /* TS Current Sensed Temperature - High Byte */ + __u8 MR51; /* TS Temperature Status */ + /* [3] TS < CRIT_LOW */ + /* [2] TS > CRIT_HIGH */ + /* [1] TS < LOW */ + /* [0] TS > HIGH */ + __u8 MR52; /* Hub, Thermal and NVM Error Status */ + /* [7] BUSY_ERROR */ + /* [6] WR_NVM_BLK_ERROR */ + /* [5] WR_NVM_PRO_REG_ERROR */ + /* [1] PEC_ERROR */ + /* [0] PAR_ERROR */ + + SPD5118_TASK_t *pOpListHead; /* op list for a specified device */ + + __u16 initMode; +}; + + #define LSM6DSO_DEVICE_INFO_t struct LSM6DSO_DEVICE_INFO /* PENDINT definition */ diff --git a/tests/boards/npcm400f_evb/boards/npcm400f_evb.conf b/tests/boards/npcm400f_evb/boards/npcm400f_evb.conf new file mode 100644 index 00000000000000..15ea997339cb70 --- /dev/null +++ b/tests/boards/npcm400f_evb/boards/npcm400f_evb.conf @@ -0,0 +1,14 @@ + + +# PECI driver +CONFIG_PECI=n +CONFIG_PECI_NPCM4XX=n + +CONFIG_I3C=y +CONFIG_I3C_NPCM4XX=y + +CONFIG_I3C_SLAVE=y +CONFIG_I3C_SLAVE_MQUEUE=y + +#CONFIG_TIMESLICING=y +#CONFIG_TIMESLICE_SIZE=1 \ No newline at end of file diff --git a/tests/boards/npcm400f_evb/boards/npcm400f_evb.overlay b/tests/boards/npcm400f_evb/boards/npcm400f_evb.overlay new file mode 100644 index 00000000000000..f2a567900cf471 --- /dev/null +++ b/tests/boards/npcm400f_evb/boards/npcm400f_evb.overlay @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Nuvoton Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&i3c0 { + status = "okay"; + assigned-address = <0x20>; + part-id = <0x1234>; + vendor-def-id = <0x567>; + bcr = <0x66>; + dcr = <0xCC>; + busno = <0x0>; + pinctrl-0 = <&pinctrl_i3c0_default>; + i2c-scl-hz = <100000>; + i3c-scl-hz = <12500000>; + + /*test-i3c-dev@420000ABCD12345678 {*/ + /* compatible = "vnd,i3c-device";*/ + /* label = "TEST_I3C_DEV_42";*/ + /* reg = < 0x42 0xABCD 0x12345678 >;*/ + /*};*/ + + /*test-i3c-i2c-dev@380000000000000050 {*/ + /* compatible = "vnd,i3c-i2c-device";*/ + /* label = "TEST_I3C_I2C_DEV_38";*/ + /* reg = < 0x38 0x0 0x50 >;*/ + /*};*/ +}; + +&i3c1 { + status = "okay"; + slave; + secondary; + assigned-address = <0x21>; + part-id = <0x1234>; + vendor-def-id = <0x567>; + bcr = <0x66>; + dcr = <0xCC>; + busno = <0x0>; + pinctrl-0 = <&pinctrl_i3c1_default>; + i2c-scl-hz = <100000>; + i3c-scl-hz = <12500000>; + /* ibi-append-pec; */ + /* priv-xfer-pec; */ + + /* #address-cells = <0x01> */ + /* #size-cells = <0x00> */ + + i3c1_smq:i3c-slave-mqueue@21 { + compatible = "i3c-slave-mqueue"; + reg = <0x21>; + msg-size = <256>; + num-of-msgs = <4>; + mandatory-data-byte = <0xAE>; + label = "I3C_1_SMQ"; + status = "okay"; + }; +}; \ No newline at end of file diff --git a/tests/boards/npcm400f_evb/prj.conf b/tests/boards/npcm400f_evb/prj.conf index 4a276847072a3e..f707aaf204378d 100644 --- a/tests/boards/npcm400f_evb/prj.conf +++ b/tests/boards/npcm400f_evb/prj.conf @@ -13,15 +13,15 @@ CONFIG_MAIN_STACK_SIZE=16384 CONFIG_ISR_STACK_SIZE=8192 CONFIG_ZTEST_STACKSIZE=16384 CONFIG_IDLE_STACK_SIZE=4096 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=16384 CONFIG_TEST_EXTRA_STACKSIZE=8192 CONFIG_PRIVILEGED_STACK_SIZE=4096 #CONFIG_SHELL_STACK_SIZE=8192 CONFIG_NO_OPTIMIZATIONS=y -CONFIG_HEAP_MEM_POOL_SIZE=16384 -CONFIG_STACK_SENTINEL=y +CONFIG_HEAP_MEM_POOL_SIZE=65536 +#CONFIG_STACK_SENTINEL=y CONFIG_LOG=y CONFIG_THREAD_ANALYZER=y diff --git a/tests/boards/npcm400f_evb/src/i3c.c b/tests/boards/npcm400f_evb/src/i3c.c index ea5e3ce153de8d..3410318f43284c 100644 --- a/tests/boards/npcm400f_evb/src/i3c.c +++ b/tests/boards/npcm400f_evb/src/i3c.c @@ -18,207 +18,223 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define TEST_PRIV_XFER_SIZE 256 -#define TEST_IBI_PAYLOAD_SIZE 128 -#define MAX_DATA_SIZE 256 +#define TEST_IBI_PAYLOAD_SIZE 256 +#define MAX_DATA_SIZE 256 /* < config->msg_size;*/ -static uint8_t test_data_tx[MAX_DATA_SIZE]; -static uint8_t test_data_rx[MAX_DATA_SIZE]; +/* external reference */ +extern int i3c_slave_mqueue_read(const struct device *dev, uint8_t *dest, int budget); +extern int i3c_slave_mqueue_write(const struct device *dev, uint8_t *src, int size); -/*#define TEST_LOOPBACK*/ -#define TEST_WITH_LSM6DSO -#define TEST_DBG +#define TEST_I3C_SLAVE_THREAD_STACK_SIZE 1024 +#define TEST_I3C_SLAVE_THREAD_PRIO CONFIG_ZTEST_THREAD_PRIORITY -static void test_i3c_ci(int count) +K_THREAD_STACK_DEFINE(test_i3c_slave_thread_stack_area, TEST_I3C_SLAVE_THREAD_STACK_SIZE); +static struct k_thread test_i3c_slave_thread; + +static uint8_t test_data_tx_mst[MAX_DATA_SIZE]; +static uint8_t test_data_rx_mst[MAX_DATA_SIZE]; +static uint8_t test_data_tx_slv[MAX_DATA_SIZE]; +static uint8_t test_data_rx_slv[MAX_DATA_SIZE]; +static struct i3c_ibi_payload i3c_payload; +static struct k_sem ibi_complete; +static volatile uint8_t mdb; + +static struct i3c_ibi_payload *test_ibi_write_requested(struct i3c_dev_desc *desc) { - const struct device *dev_master, *dev_slave; - const struct device *dev = NULL; - struct i3c_dev_desc *slave = NULL; - struct i3c_priv_xfer xfer[2]; - int ret, i; + /* reset rx buffer */ + /* memset(test_data_rx_mst, 0x00, sizeof(test_data_rx_mst)); */ -#if defined(TEST_LOOPBACK) - struct i3c_dev_desc slave_i3c; + i3c_payload.buf = test_data_rx_mst; + i3c_payload.size = 0; + i3c_payload.max_payload_size = MAX_DATA_SIZE; - slave = &slave_i3c; -#elif defined(TEST_WITH_LSM6DSO) - struct i3c_dev_desc slave_lsm6dso; + return &i3c_payload; +} - slave = &slave_lsm6dso; -#endif +static void test_ibi_write_done(struct i3c_dev_desc *desc) +{ + if (IS_MDB_PENDING_READ_NOTIFY(mdb)) { + k_sem_give(&ibi_complete); + } +} - dev_master = device_get_binding(DT_LABEL(DT_NODELABEL(i3c0))); - dev_slave = device_get_binding(DT_LABEL(DT_NODELABEL(i3c1))); +static struct i3c_ibi_callbacks i3c_ibi_def_callbacks = { + .write_requested = test_ibi_write_requested, + .write_done = test_ibi_write_done, +}; - /* Not ready, do not use */ -#ifndef TEST_DBG - if (!device_is_ready(dev_master) || !device_is_ready(dev_slave)) - return; -#else - if (dev_master == NULL) { - dev = DEVICE_DT_GET(DT_NODELABEL(i3c0)); - if (dev == NULL) { - LOG_INF("I3C0 is not found in device tree !\n"); - return; +static void prepare_test_data(uint8_t *data, int nbytes) +{ + uint32_t value; + int i, shift; + + value = sys_rand32_get(); + for (i = 0; i < nbytes; i++) { + shift = (i & 0x3) * 8; + data[i] = (value >> shift) & 0xff; + if ((i & 0x3) == 0x3) { + value = sys_rand32_get(); } + } +} - if (!device_is_ready(dev)) { - if (dev->state->initialized == false) { - LOG_INF("I3C0 is found in device tree, but not init !\n"); - return; +static void test_i3c_slave_task(void *arg1, void *arg2, void *arg3) +{ + const struct device *slave_mq = (const struct device *)arg1; + int ret = 0; + + int counter = 0; /* for debug only */ + + for (;;) { + /* Test part 1: read and compare the data */ + + /* reset rx buffer */ + memset(test_data_rx_slv, 0x00, sizeof(test_data_rx_slv)); + + while (1) { + ret = i3c_slave_mqueue_read(slave_mq, (uint8_t *)test_data_rx_slv, + TEST_PRIV_XFER_SIZE); + if (ret) { + if (memcmp(test_data_tx_mst, test_data_rx_slv, TEST_PRIV_XFER_SIZE) + == 0) { + break; + } + LOG_WRN("$"); } - LOG_INF("I3C0 is initialized, but fail !\n"); - return; + k_sleep(K_USEC(1)); } + + ast_zassert_mem_equal(test_data_tx_mst, test_data_rx_slv, TEST_PRIV_XFER_SIZE, + "i3c private write test fail: data mismatch %d %X %X", counter, + test_data_tx_mst[0], test_data_rx_slv[0]); + + /* Test part 2: send IBI to notify the master device to get the pending data */ + /* prepare_test_data(test_data_tx_slv, TEST_IBI_PAYLOAD_SIZE); */ + + /* for debug only */ + memcpy(test_data_tx_slv, test_data_rx_slv, TEST_IBI_PAYLOAD_SIZE); + ret = i3c_slave_mqueue_write(slave_mq, test_data_tx_slv, TEST_IBI_PAYLOAD_SIZE); + ast_zassert_equal(ret, 0, "failed to do slave mqueue write"); + counter++; } +} - if (dev_slave == NULL) { - dev = DEVICE_DT_GET(DT_NODELABEL(i3c1)); - if (dev == NULL) { - LOG_INF("I3C1 is not found in device tree !\n"); - return; - } +static void test_i3c_ci(int count) +{ + const struct device *dev_master, *dev_slave, *slave_mq; + struct i3c_dev_desc slave_i3c; + struct i3c_dev_desc *slave; + struct i3c_priv_xfer xfer[2]; + int ret, i; - if (!device_is_ready(dev)) { - if (dev->state->initialized == false) { - LOG_INF("I3C1 is found in device tree, but not initialized !\n"); - return; - } + dev_master = device_get_binding(DT_LABEL(DT_NODELABEL(i3c0))); + dev_slave = device_get_binding(DT_LABEL(DT_NODELABEL(i3c1))); + slave_mq = device_get_binding(DT_LABEL(DT_NODELABEL(i3c1_smq))); - LOG_INF("I3C1 is initialized, but fail !\n"); - return; - } + /* Not ready, do not use */ + if (!device_is_ready(dev_master) || !device_is_ready(dev_slave) || + !device_is_ready(slave_mq)) { + return; } -#endif -#if defined(TEST_LOOPBACK) - slave->info.static_addr = DT_PROP(DT_NODELABEL(i3c1), assigned_address); + /* prepare slave device info for attach */ + slave = &slave_i3c; + slave->info.static_addr = DT_PROP(DT_BUS(DT_NODELABEL(i3c1_smq)), assigned_address); slave->info.assigned_dynamic_addr = slave->info.static_addr; + slave->info.pid = 0x063212341567; slave->info.i2c_mode = 1; /* default run i2c mode */ -#elif defined(TEST_WITH_LSM6DSO) - /* attach lsm6dso */ - #define LSM6DSO_ADDR 0x6B - slave->info.static_addr = LSM6DSO_ADDR; - slave->info.assigned_dynamic_addr = LSM6DSO_ADDR; - slave->info.i2c_mode = 1; -#endif + + /* example to attach lsm6dso */ + /* + * #define LSM6DSO_ADDR 0x6B + * + * slave->info.static_addr = LSM6DSO_ADDR; + * slave->info.assigned_dynamic_addr = LSM6DSO_ADDR; + * slave->info.i2c_mode = 1; + */ if (slave == NULL) return; i3c_master_attach_device(dev_master, slave); + + /* try to enter i3c mode */ + /* assign dynamic address with setdasa or entdaa, doesn't support setaasa in slave mode */ i3c_master_send_rstdaa(dev_master); + i3c_master_send_aasa(dev_master); /* Compatibility test for JESD403 */ + i3c_master_send_entdaa(slave); + slave->info.i2c_mode = 0; - for (i = 0; i < count; i++) { - /* - * Test part 1: - * master --- i2c private write transfer ---> slave - */ - xfer[0].rnw = 0; - xfer[0].len = 1; - xfer[0].data.out = test_data_tx; - test_data_tx[0] = 0x0F; + /* must wait for a while, for slave finish sir_allowed_worker ? */ + /* k_sleep(K_USEC(1)); */ - ret = i3c_master_priv_xfer(slave, xfer, 1); + /* try to collect more slave info if called setaasa in the former */ + i3c_master_send_getpid(dev_master, slave->info.dynamic_addr, &slave->info.pid); + i3c_master_send_getbcr(dev_master, slave->info.dynamic_addr, &slave->info.bcr); - /* - * Test part 2: - * master --- i2c private read transfer ---> slave - */ - xfer[0].rnw = 1; - xfer[0].len = 1; - xfer[0].data.in = test_data_rx; + /* setup callback function to receive mdb */ + ret = i3c_master_request_ibi(slave, &i3c_ibi_def_callbacks); + ast_zassert_equal(ret, 0, "failed to request sir"); - ret = i3c_master_priv_xfer(slave, xfer, 1); - __ASSERT(test_data_rx[0] == 0x6C, "Read Data Fail !!!\n\n"); + /* sent enec to slave to enable ibi feature */ + ret = i3c_master_enable_ibi(slave); + ast_zassert_equal(ret, 0, "failed to enable sir"); - /* - * Test part 3: for those who support stopsplitread - * master --- i2c private write then read transfer ---> slave - */ - memset(test_data_rx, 0x00, sizeof(test_data_rx)); + /* get request from message queue and write back response message with ibi */ + k_thread_create(&test_i3c_slave_thread, test_i3c_slave_thread_stack_area, + TEST_I3C_SLAVE_THREAD_STACK_SIZE, test_i3c_slave_task, (void *)slave_mq, + NULL, NULL, TEST_I3C_SLAVE_THREAD_PRIO, 0, K_NO_WAIT); - i3c_i2c_read(slave, 0x0F, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0x6C, "Read Data Fail !!!\n\n"); + mdb = DT_PROP(DT_NODELABEL(i3c1_smq), mandatory_data_byte); - /* - * Test part 4: - * master --- i2c API ---> slave - */ - test_data_tx[0] = 0xC0; - ret = i3c_i2c_write(slave, 0x01, test_data_tx, 1); - ret = i3c_i2c_read(slave, 0x01, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0xC0, "Read Data Fail !!!\n\n"); - - test_data_tx[0] = 0x00; - ret = i3c_i2c_write(slave, 0x01, test_data_tx, 1); - ret = i3c_i2c_read(slave, 0x01, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0x00, "Read Data Fail !!!\n\n"); + /* create semaphore to synchronize master and slave between ibi */ + if (IS_MDB_PENDING_READ_NOTIFY(mdb)) { + k_sem_init(&ibi_complete, 0, 1); } - i3c_master_send_entdaa(slave); - /* i3c_master_send_aasa(master); */ - slave->info.i2c_mode = 0; - - i3c_master_send_getpid(dev_master, slave->info.dynamic_addr, &slave->info.pid); - i3c_master_send_getbcr(dev_master, slave->info.dynamic_addr, &slave->info.bcr); - for (i = 0; i < count; i++) { - /* - * Test part 1: - * master --- i3c private write transfer ---> slave - */ - xfer[0].rnw = 0; - xfer[0].len = 1; - xfer[0].data.out = test_data_tx; - test_data_tx[0] = 0x0F; - ret = i3c_master_priv_xfer(slave, xfer, 1); - - /* - * Test part 2: - * master --- i3c private read transfer ---> slave - */ - xfer[0].rnw = 1; - xfer[0].len = 1; - xfer[0].data.in = test_data_rx; + /* prepare request message */ + prepare_test_data(test_data_tx_mst, TEST_PRIV_XFER_SIZE); + /* test_data_tx_mst[0] = i; */ /* for debug only */ + /* k_usleep(100); */ /* debug only */ + /* Requester send request message */ + xfer[0].rnw = 0; + xfer[0].len = TEST_PRIV_XFER_SIZE; + xfer[0].data.out = test_data_tx_mst; ret = i3c_master_priv_xfer(slave, xfer, 1); - __ASSERT(test_data_rx[0] == 0x6C, "Read Data Fail !!!\n\n"); - - /* - * Test part 3: for those who support stopsplitread - * master --- i3c private write then read transfer ---> slave - */ - memset(test_data_rx, 0x00, sizeof(test_data_rx)); - - test_data_tx[0] = 0x0F; /* WHO_AM_I, lsm6dso */ - ret = i3c_jesd403_read(slave, test_data_tx, 1, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0x6C, "Read Data Fail !!!\n\n"); /* - * Test part 4: - * master --- i3c API ---> slave - */ - memset(test_data_rx, 0x00, sizeof(test_data_rx)); - test_data_tx[0] = 0x01; - test_data_tx[1] = 0xC0; - ret = i3c_jesd403_write(slave, test_data_tx, 1, &test_data_tx[1], 1); - ret = i3c_jesd403_read(slave, test_data_tx, 1, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0xC0, "Read Data Fail !!!\n\n"); - - test_data_tx[1] = 0x00; - ret = i3c_jesd403_write(slave, test_data_tx, 1, &test_data_tx[1], 1); - ret = i3c_jesd403_read(slave, test_data_tx, 1, test_data_rx, 1); - __ASSERT(test_data_rx[0] == 0x00, "Read Data Fail !!!\n\n"); - - /* - * Test part 2: * if MDB group ID is pending read notification: * master <--- IBI notification --------- slave * master ---- private read transfer ---> slave * else: * master <--- IBI with data --------- slave */ + + if (IS_MDB_PENDING_READ_NOTIFY(mdb)) { + /* master waits IBI from the slave */ + k_sem_take(&ibi_complete, K_FOREVER); + + /* init the flag for the next loop */ + k_sem_init(&ibi_complete, 0, 1); + + /* check result */ + ast_zassert_equal(mdb, test_data_rx_mst[0], + "IBI MDB mismatch: %02x %02x\n", + ret, test_data_rx_mst[0]); + } + + /* initiate a private read transfer to read the pending data */ + xfer[0].rnw = 1; + xfer[0].len = TEST_IBI_PAYLOAD_SIZE; + xfer[0].data.in = test_data_rx_mst; + k_yield(); + ret = i3c_master_priv_xfer(slave, &xfer[0], 1); + ast_zassert_mem_equal(test_data_tx_slv, test_data_rx_mst, TEST_IBI_PAYLOAD_SIZE, + "data mismatch %d %X %X", i, test_data_tx_slv[0], test_data_rx_mst[0]); + /*k_usleep(100);*/ /* debug only */ } } @@ -231,6 +247,5 @@ int test_i3c(int count, enum aspeed_test_type type) return ast_ztest_result(); } - /* Not support FT yet */ return AST_TEST_PASS; } diff --git a/tests/boards/npcm400f_evb/src/main.c b/tests/boards/npcm400f_evb/src/main.c index bc3868e5c4b350..17bfb08b627b02 100644 --- a/tests/boards/npcm400f_evb/src/main.c +++ b/tests/boards/npcm400f_evb/src/main.c @@ -18,13 +18,13 @@ extern void test_i3c(void); nuvoton_run_test_suite(#suite, _##suite, type) #define TEST_MODULE_CNT 1 -#define TEST_STACKSIZE 4096 +#define TEST_STACKSIZE 8192 #define TEST_CI_TIMEOUT 40 #define TEST_SLT_TIMEOUT 20 #define TEST_FT_TIMEOUT 5 -#define TEST_CI_FUNC_COUNT 1 +#define TEST_CI_FUNC_COUNT 100 #define TEST_SLT_FUNC_COUNT 1 #define TEST_FT_FUNC_COUNT 1