// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include "ccu_ipc.h" #include "ccu_cmn.h" #include "ccu_reg.h" #include "ccu_platform_def.h" static DEFINE_MUTEX(ccu_ipc_mutex); #define CCU_IPC_NO_ACK 0x66669999 #define CCU_IPC_CMD_TIMEOUT_SPEC 3000 //3000us = 3ms struct CCU_IPC_DATA { MBOOL m_initialized; uint32_t *m_ccuIntTriggerPtr; struct ap2ccu_ipc_t *m_ccuIpcPtr; void *m_ipcInDataPtr; void *m_ipcOutDataPtr; uint32_t m_ipcInDataAddrCcu; uint32_t m_ipcOutDataAddrCcu; uint64_t ccu_base; }; static struct CCU_IPC_DATA ccu_ipc; void ccu_ipc_init(unsigned int *ccuDmBase, unsigned int *ccuCtrlBase) { struct shared_buf_map *sb_map_ptr = (struct shared_buf_map *)(ccuDmBase + OFFSET_CCU_SHARED_BUF_MAP_BASE); ccu_ipc.m_ccuIntTriggerPtr = (uint32_t *)ccuCtrlBase + OFFSET_CCU_INT_TRG; ccu_ipc.m_ccuIpcPtr = (struct ap2ccu_ipc_t *)(((uint8_t *)ccuDmBase) + sb_map_ptr->ipc_base_offset); ccu_ipc.m_ipcInDataPtr = (void *)(((uint8_t *)ccuDmBase) + sb_map_ptr->ipc_in_data_base_offset); ccu_ipc.m_ipcOutDataPtr = (void *)(((uint8_t *)ccuDmBase) + sb_map_ptr->ipc_out_data_base_offset); ccu_ipc.m_ipcInDataAddrCcu = sb_map_ptr->ipc_in_data_addr_ccu; ccu_ipc.m_ipcOutDataAddrCcu = sb_map_ptr->ipc_out_data_addr_ccu; ccu_ipc.m_initialized = MTRUE; ccu_ipc.ccu_base = (unsigned long)ccuCtrlBase; LOG_DBG("ccuDmBase: %p", ccuDmBase); LOG_DBG("ccuCtrlBase: %p", ccuCtrlBase); LOG_DBG("m_ccuIntTriggerPtr: %p", ccu_ipc.m_ccuIntTriggerPtr); LOG_DBG("m_ccuIpcPtr: %p", ccu_ipc.m_ccuIpcPtr); LOG_DBG("m_ipcInDataPtr: %p", ccu_ipc.m_ipcInDataPtr); LOG_DBG("m_ipcOutDataPtr: %p", ccu_ipc.m_ipcOutDataPtr); } bool ccu_ipc_getIObuffer(void **ipcInDataPtr, void **ipcOutDataPtr, uint32_t *ipcInDataAddrCcu, uint32_t *ipcOutDataAddrCcu) { LOG_DBG("+"); if (ccu_ipc.m_initialized == MFALSE) { LOG_ERR("not initialized, invalid operation."); return MFALSE; } *ipcInDataPtr = ccu_ipc.m_ipcInDataPtr; *ipcOutDataPtr = ccu_ipc.m_ipcOutDataPtr; *ipcInDataAddrCcu = ccu_ipc.m_ipcInDataAddrCcu; *ipcOutDataAddrCcu = ccu_ipc.m_ipcOutDataAddrCcu; LOG_DBG("*ipcInDataPtr: %p", *ipcInDataPtr); LOG_DBG("*ipcOutDataPtr: %p", *ipcOutDataPtr); LOG_DBG("*ipcInDataAddrCcu: %p", *ipcInDataAddrCcu); LOG_DBG("*ipcOutDataAddrCcu: %p", *ipcOutDataAddrCcu); LOG_DBG("-"); return MTRUE; } int ccu_ipc_send(struct ccu_msg *msg) { uint32_t loopCount = 0; bool ackValue = 0; bool timeout = false; uint32_t write_cnt = 0; uint32_t read_cnt = 0; LOG_DBG("+, ftype(%d), msg_id(%d)", msg->feature_type, msg->msg_id); if (ccu_ipc.m_initialized == MFALSE) { LOG_ERR("not initialized, invalid operation."); return -1; } LOG_DBG("+, read_cnt1,%x", &ccu_ipc.m_ccuIpcPtr); //since ipc is synchronized, check no previous ipc write_cnt = readl(&(ccu_ipc.m_ccuIpcPtr->write_cnt)); read_cnt = readl(&(ccu_ipc.m_ccuIpcPtr->read_cnt)); if (read_cnt != write_cnt) { LOG_ERR("CCU IPC synchronization violation, rcnt:%d, wcnt:%d", read_cnt, write_cnt); return -1; } LOG_DBG("+, read_cnt2"); writel(CCU_IPC_NO_ACK, &ccu_ipc.m_ccuIpcPtr->ack); write_cnt = readl(&ccu_ipc.m_ccuIpcPtr->write_cnt); writel(write_cnt + 1, &ccu_ipc.m_ccuIpcPtr->write_cnt); LOG_DBG("+set msg"); writel(msg->feature_type, &ccu_ipc.m_ccuIpcPtr->msg.feature_type); writel(msg->msg_id, &ccu_ipc.m_ccuIpcPtr->msg.msg_id); writel(msg->in_data_ptr, &ccu_ipc.m_ccuIpcPtr->msg.in_data_ptr); writel(msg->out_data_ptr, &ccu_ipc.m_ccuIpcPtr->msg.out_data_ptr); writel(msg->tg_info, &ccu_ipc.m_ccuIpcPtr->msg.tg_info); writel(msg->sensor_idx, &ccu_ipc.m_ccuIpcPtr->msg.sensor_idx); LOG_DBG("+, CCU IPC TRIG"); ccu_write_reg_bit(ccu_ipc.ccu_base, EXT2CCU_INT_CCU, EXT2CCU_INT_CCU, 1); while (readl(&ccu_ipc.m_ccuIpcPtr->ack) == CCU_IPC_NO_ACK) { loopCount++; udelay(100); if (loopCount > CCU_IPC_CMD_TIMEOUT_SPEC) { ackValue = readl(&ccu_ipc.m_ccuIpcPtr->ack); if (ackValue == CCU_IPC_NO_ACK) timeout = true; else LOG_DBG_MUST("CCU cmd timeout false alarm"); break; } } if (timeout) { LOG_ERR("CCU cmd timeout"); LOG_ERR("ftype(%d), msg_id(%d), ackValue(%d), loopCount(%d)", msg->feature_type, msg->msg_id, ackValue, loopCount); return -1; } LOG_DBG("-, ftype(%d), msg_id(%d), loopCount(%d)", msg->feature_type, msg->msg_id, loopCount); return 0; } bool _copyCmdInData(struct CcuIOBufInfo *inDataBufInfo, void *inDataPtr, uint32_t inDataSize) { uint32_t i; LOG_DBG("+:%s\n", __func__); if (inDataBufInfo->bufType == CCU_SRAM_IO) { // copy to sram uint32_t *realIoBuf = (uint32_t *)inDataBufInfo->addr_ap; //copy from _interIBuf to real IO buffer in unit of 4bytes, //to avoid APB bus constraint of non-4byte-aligned data access for (i = 0; i < (inDataSize/4); i++) { // realIoBuf[i] = ((uint32_t *)inDataPtr)[i]; writel(((uint32_t *)inDataPtr)[i], &realIoBuf[i]); } } LOG_DBG("-:%s\n", __func__); return true; } bool _copyCmdOutData(struct CcuIOBufInfo *outDataBufInfo, void *outDataPtr, uint32_t outDataSize) { uint32_t i; LOG_DBG("+:%s\n", __func__); if (outDataBufInfo->bufType == CCU_SRAM_IO) { uint32_t *realIoBuf = (uint32_t *)outDataBufInfo->addr_ap; //copy from _interIBuf to real IO buffer in unit of 4bytes, //to avoid APB bus constraint of non-4byte-aligned data access for (i = 0; i < (outDataSize/4); i++) { // ((uint32_t *)outDataPtr)[i] = realIoBuf[i]; writel(realIoBuf[i], &((uint32_t *)outDataPtr)[i]); } } LOG_DBG("-:%s\n", __func__); return MTRUE; } bool _resolveCmdIOBuf(struct CcuIOBufInfo *inDataBufInfo, struct CcuIOBufInfo *outDataBufInfo, uint32_t inDataSize, uint32_t outDataSize) { if (!ccu_ipc_getIObuffer((void **)&inDataBufInfo->addr_ap, (void **)&outDataBufInfo->addr_ap, &inDataBufInfo->addr_ccu, &outDataBufInfo->addr_ccu)) { return false; } LOG_DBG("inDataSize(%d), sramInBufCapacity(%d)", inDataSize, CCU_IPC_IBUF_CAPACITY); LOG_DBG("outDataSize(%d), sramOutBufCapacity(%d)", outDataSize, CCU_IPC_OBUF_CAPACITY); // Resolve Input buffer if (inDataSize <= CCU_IPC_IBUF_CAPACITY) { LOG_DBG("using Sram input buffer"); inDataBufInfo->bufType = CCU_SRAM_IO; } else { LOG_ERR("inDataSize excceed buffer capacity., sramBufCap(%d)", CCU_IPC_IBUF_CAPACITY); return false; } LOG_DBG("inDataBufInfo.addr_ap: %p, addr_ccu: %p", inDataBufInfo->addr_ap, inDataBufInfo->addr_ccu); // Resolve Output buffer if (outDataSize <= CCU_IPC_OBUF_CAPACITY) { LOG_DBG("using Sram output buffer"); outDataBufInfo->bufType = CCU_SRAM_IO; } else { LOG_ERR("outDataSize excceed buffer capacity."); return false; } LOG_DBG("outDataBufInfo.addr_ap: %p, addr_ccu: %p", outDataBufInfo->addr_ap, outDataBufInfo->addr_ccu); return true; } bool ccuControl( enum ccu_feature_type featureType, enum IMGSENSOR_SENSOR_IDX sensorIdx, uint32_t msgId, void *inDataPtr, uint32_t inDataSize, void *outDataPtr, uint32_t outDataSize) { struct CcuIOBufInfo inDataBufInfo; struct CcuIOBufInfo outDataBufInfo; struct ccu_msg msg = {featureType, msgId, inDataBufInfo.addr_ccu, outDataBufInfo.addr_ccu, CCU_CAM_TG_NONE, sensorIdx}; uint32_t ret; mutex_lock(&ccu_ipc_mutex); LOG_DBG("[%s]+: ft(%d), sIdx(%d), msgId(%d)\n", __func__, featureType, sensorIdx, msgId); //resolve IO buf ret = _resolveCmdIOBuf(&inDataBufInfo, &outDataBufInfo, inDataSize, outDataSize); if (ret == MFALSE) { LOG_ERR("resolveCmdIOBuf failed, cmd abort."); mutex_unlock(&ccu_ipc_mutex); return false; } //Check if need input data if (inDataSize != 0) { //copy input data to IPC buffer & command data buffer _copyCmdInData(&inDataBufInfo, inDataPtr, inDataSize); } msg.in_data_ptr = inDataBufInfo.addr_ccu; msg.out_data_ptr = outDataBufInfo.addr_ccu; ret = ccu_ipc_send(&msg); if (ret != 0) { LOG_ERR("sendCcuCommnadIpc failed, msgId(%d)", msgId); mutex_unlock(&ccu_ipc_mutex); return false; } //check if need to copy output data if (outDataSize != 0) _copyCmdOutData(&outDataBufInfo, outDataPtr, outDataSize); LOG_DBG("-:\n", __func__); mutex_unlock(&ccu_ipc_mutex); return true; }