kernel-brax3-ubuntu-touch/drivers/remoteproc/mtk_ccu_ipi.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

476 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2021 MediaTek Inc.
#include <linux/clocksource.h>
#include <linux/remoteproc/mtk_ccu.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include "mtk_ccu_common.h"
#include "mtk_ccu_isp71.h"
#if defined(SECURE_CCU)
#include <linux/soc/mediatek/mtk_sip_svc.h>
#include <linux/arm-smccc.h>
#endif
#define MTK_CCU_IPI_TAG "[ccu_ipc]"
#define LOG_DBG_IPI(format, args...) \
pr_info(MTK_CCU_IPI_TAG "[%s] " format, __func__, ##args)
#define MTK_CCU_IPC_NO_ACK 0x66669999
#define MTK_CCU_IPC_CMD_TIMEOUT_SPEC 30 //3000us = 3ms
#define MTK_CCU_IPC_IOBUF_CAPACITY (1024*4-16-64-64)
struct __aligned(8) shared_buf_map
{
/*** from CCU->APMCU ***/
uint32_t ipc_data_addr_ccu;
uint32_t ipc_data_base_offset;
uint32_t ipc_base_offset;
uint32_t ipc_data_size;
};
static int mtk_ccu_copyCmdInData(struct mtk_ccu *ccu,
void *inDataPtr, uint32_t inDataSize)
{
uint32_t i;
uint32_t *realIoBuf = (uint32_t *)ccu->ccu_ipc.ipcDataPtr;
if (inDataSize == 0)
return 0;
if (inDataSize >= ccu->ccu_ipc.ipcDataSize) {
dev_err(ccu->dev, "failed: data length over capacity(%d <= %d)\n",
ccu->ccu_ipc.ipcDataSize, inDataSize);
return -EINVAL;
}
for (i = 0; i < (inDataSize/4) ; i++)
writel(((uint32_t *)inDataPtr)[i], &realIoBuf[i]);
return 0;
}
static int mtk_ccu_copyCmdOutData(struct mtk_ccu *ccu,
void *outDataPtr, uint32_t outDataSize)
{
uint32_t i;
uint32_t *realIoBuf = (uint32_t *)ccu->ccu_ipc.ipcDataPtr;
if (outDataSize == 0)
return 0;
if (outDataSize >= ccu->ccu_ipc.ipcDataSize) {
dev_err(ccu->dev, "failed: data length over capacity(%d <= %d)\n",
ccu->ccu_ipc.ipcDataSize, outDataSize);
return -EINVAL;
}
for (i = 0; i < (outDataSize/4); i++)
writel(realIoBuf[i], &((uint32_t *)outDataPtr)[i]);
return 0;
}
static int mtk_ccu_rproc_ipc_trigger(struct mtk_ccu *ccu,
struct mtk_ccu_msg *msg)
{
uint32_t loop_cnt = 0;
bool ackValue = 0;
bool timeout = false;
uint32_t write_cnt = 0;
uint32_t read_cnt = 0;
/* LOG_DBG_IPI("+, ftype(%d), msg_id(%d)", msg->feature_type, msg->msg_id); */
if (ccu->ccu_ipc.is_initialized == false) {
dev_err(ccu->dev, "IPC not initialized, invalid operation.");
return -EINVAL;
}
write_cnt = readl(&ccu->ccu_ipc.ccuIpcPtr->write_cnt);
read_cnt = readl(&ccu->ccu_ipc.ccuIpcPtr->read_cnt);
//since ipc is synchronized, check no previous ipc
if (read_cnt != write_cnt) {
dev_err(ccu->dev,
"CCU IPC violation, rcnt:%d, wcnt:%d, tout_fid:%d, tout_mid:%d",
read_cnt, write_cnt, ccu->ipc_tout_fid, ccu->ipc_tout_mid);
dev_err(ccu->dev, "r_assert:0x%x, i28:0x%x, i29:0x%x, i30:0x%x, i31:0x%x",
readl(ccu->ccu_base + MTK_CCU_SPARE_REG20),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG28),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG29),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG30),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG31));
return -EINVAL;
}
writel(MTK_CCU_IPC_NO_ACK, &ccu->ccu_ipc.ccuIpcPtr->ack);
write_cnt = readl(&ccu->ccu_ipc.ccuIpcPtr->write_cnt);
writel(write_cnt + 1, &ccu->ccu_ipc.ccuIpcPtr->write_cnt);
writel(msg->feature_type, &ccu->ccu_ipc.ccuIpcPtr->msg.feature_type);
writel(msg->msg_id, &ccu->ccu_ipc.ccuIpcPtr->msg.msg_id);
writel(msg->in_data_ptr, &ccu->ccu_ipc.ccuIpcPtr->msg.in_data_ptr);
writel(msg->tg_info, &ccu->ccu_ipc.ccuIpcPtr->msg.tg_info);
writel(msg->sensor_idx, &ccu->ccu_ipc.ccuIpcPtr->msg.sensor_idx);
writel(1, ccu->ccu_ipc.ccuIntTrigPtr);
while (readl(&ccu->ccu_ipc.ccuIpcPtr->ack) == MTK_CCU_IPC_NO_ACK) {
loop_cnt++;
udelay(100);
if (loop_cnt > MTK_CCU_IPC_CMD_TIMEOUT_SPEC) {
timeout = true;
break;
}
}
if (timeout) {
dev_err(ccu->dev,
"CCU IPC timeout, ft(%d), mid(%d), ack(%d), cnt(%d)\n",
msg->feature_type, msg->msg_id, ackValue, loop_cnt);
dev_err(ccu->dev, "r_assert:0x%x, i28:0x%x, i29:0x%x, i30:0x%x, i31:0x%x",
readl(ccu->ccu_base + MTK_CCU_SPARE_REG20),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG28),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG29),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG30),
readl(ccu->ccu_base + MTK_CCU_SPARE_REG31));
ccu->ipc_tout_fid = msg->feature_type;
ccu->ipc_tout_mid = msg->msg_id;
return -ETIMEDOUT;
}
/*
* LOG_DBG_IPI("-, ftype(%d), msg_id(%d), loop_cnt(%d), ackValue(%d)",
* msg->feature_type, msg->msg_id, loop_cnt, ackValue);
*/
return 0;
}
static int mtk_ccu_mb_rx(struct mtk_ccu *ccu,
struct mtk_ccu_msg *task)
{
int ret = 0;
uint32_t rear;
uint32_t front;
uint32_t next;
if (!spin_trylock(&ccu->ccu_poweron_lock))
return 0;
if (!ccu->poweron) {
spin_unlock(&ccu->ccu_poweron_lock);
return 0;
}
rear = readl(&ccu->mb->rear);
front = readl(&ccu->mb->front);
/*Check if queue is empty*/
/*empty when front=rear*/
if (rear != front) {
/*modulus add: rear+1 = rear+1 % CCU_MAILBOX_QUEUE_SIZE*/
next =
(ccu->mb->front + 1) & (MTK_CCU_MAILBOX_QUEUE_SIZE - 1);
memcpy(task, &(ccu->mb->queue[next]),
sizeof(struct mtk_ccu_msg));
ccu->mb->front = next;
LOG_DBG_IPI(
"[%u] received cmd: f(%d), r(%d), cmd(%d), in(%x)\n",
(uint32_t)arch_timer_read_counter(),
ccu->mb->front,
ccu->mb->rear,
ccu->mb->queue[next].msg_id,
ccu->mb->queue[next].in_data_ptr);
ret = rear - front + 1;
#if IS_ENABLED(CONFIG_MTK_CCU_DEBUG)
LOG_DBG_IPI("fs[%d,%d,%d,%d,%d],sc[%d,%d,%d,%d,%d],cam[%d,%d,%d,%d,%d]\n",
ccu->rproc->bootcnt[0][0].counter, ccu->rproc->bootcnt[0][1].counter,
ccu->rproc->bootcnt[0][2].counter, ccu->rproc->bootcnt[0][3].counter,
ccu->rproc->bootcnt[0][4].counter,
ccu->rproc->bootcnt[1][0].counter, ccu->rproc->bootcnt[1][1].counter,
ccu->rproc->bootcnt[1][2].counter, ccu->rproc->bootcnt[1][3].counter,
ccu->rproc->bootcnt[1][4].counter,
ccu->rproc->bootcnt[2][0].counter, ccu->rproc->bootcnt[2][1].counter,
ccu->rproc->bootcnt[2][2].counter, ccu->rproc->bootcnt[2][3].counter,
ccu->rproc->bootcnt[2][4].counter);
LOG_DBG_IPI("senif[%d,%d,%d,%d,%d],img[%d,%d,%d,%d,%d],icmdq[%d,%d,%d,%d,%d]\n",
ccu->rproc->bootcnt[3][0].counter, ccu->rproc->bootcnt[3][1].counter,
ccu->rproc->bootcnt[3][2].counter, ccu->rproc->bootcnt[3][3].counter,
ccu->rproc->bootcnt[3][4].counter,
ccu->rproc->bootcnt[4][0].counter, ccu->rproc->bootcnt[4][1].counter,
ccu->rproc->bootcnt[4][2].counter, ccu->rproc->bootcnt[4][3].counter,
ccu->rproc->bootcnt[4][4].counter,
ccu->rproc->bootcnt[5][0].counter, ccu->rproc->bootcnt[5][1].counter,
ccu->rproc->bootcnt[5][2].counter, ccu->rproc->bootcnt[5][3].counter,
ccu->rproc->bootcnt[5][4].counter);
LOG_DBG_IPI("dvfs[%d,%d,%d,%d,%d],gce[%d,%d,%d,%d,%d]\n",
ccu->rproc->bootcnt[6][0].counter, ccu->rproc->bootcnt[6][1].counter,
ccu->rproc->bootcnt[6][2].counter, ccu->rproc->bootcnt[6][3].counter,
ccu->rproc->bootcnt[6][4].counter,
ccu->rproc->bootcnt[7][0].counter, ccu->rproc->bootcnt[7][1].counter,
ccu->rproc->bootcnt[7][2].counter, ccu->rproc->bootcnt[7][3].counter,
ccu->rproc->bootcnt[7][4].counter);
#endif
} else
ret = 0;
spin_unlock(&ccu->ccu_poweron_lock);
return ret;
}
irqreturn_t mtk_ccu_isr_handler(int irq, void *priv)
{
int mb_cnt;
static struct mtk_ccu_msg msg;
struct mtk_ccu *ccu = (struct mtk_ccu *)priv;
mtk_ccu_ipc_handle_t handler;
#if defined(SECURE_CCU)
struct arm_smccc_res res;
#endif
if (!spin_trylock(&ccu->ccu_poweron_lock)) {
LOG_DBG_IPI("trylock failed.\n");
goto ISR_EXIT;
}
if (!ccu->poweron) {
LOG_DBG_IPI("ccu->poweron false.\n");
#ifdef REQUEST_IRQ_IN_INIT
if (spin_trylock(&ccu->ccu_irq_lock)) {
if (ccu->disirq) {
ccu->disirq = false;
spin_unlock(&ccu->ccu_irq_lock);
disable_irq_nosync(ccu->irq_num);
} else
spin_unlock(&ccu->ccu_irq_lock);
}
#endif
spin_unlock(&ccu->ccu_poweron_lock);
goto ISR_EXIT;
}
/*clear interrupt status*/
#if defined(SECURE_CCU)
if (ccu->ccu_version > CCU_VER_ISP71)
writel(1, ccu->ccu_base + MTK_CCU_INT_CLR_EXCH);
else {
#ifdef CONFIG_ARM64
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u64) CCU_SMC_REQ_CLEAR_INT,
(u64)1, 0, 0, 0, 0, 0, &res);
#endif
#ifdef CONFIG_ARM_PSCI
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u32) CCU_SMC_REQ_CLEAR_INT,
(u32)1, 0, 0, 0, 0, 0, &res);
#endif
}
#else
writel(0xFF, ccu->ccu_base + MTK_CCU_INT_CLR);
// int_st = readl(ccu->ccu_base + MTK_CCU_INT_ST);
#endif
spin_unlock(&ccu->ccu_poweron_lock);
while (1) {
mb_cnt = mtk_ccu_mb_rx(ccu, &msg);
if (mb_cnt == 0) {
if (spin_trylock(&ccu->ccu_irq_lock)) {
if (ccu->disirq) {
ccu->disirq = false;
spin_unlock(&ccu->ccu_irq_lock);
disable_irq_nosync(ccu->irq_num);
} else
spin_unlock(&ccu->ccu_irq_lock);
}
goto ISR_EXIT;
}
if (msg.msg_id >= MTK_CCU_MSG_TO_APMCU_MAX)
continue;
mutex_lock(&ccu->ipc_desc_lock);
handler = ccu->ipc_desc[msg.msg_id].handler;
mutex_unlock(&ccu->ipc_desc_lock);
if (ccu->ipc_desc[msg.msg_id].handler != NULL) {
ccu->ipc_desc[msg.msg_id].handler(msg.in_data_ptr,
msg.inDataSize, ccu->ipc_desc[msg.msg_id].priv);
}
}
ISR_EXIT:
return IRQ_HANDLED;
}
void mtk_ccu_rproc_ipc_init(struct mtk_ccu *ccu)
{
uint8_t *dmbase = (uint8_t *)ccu->dmem_base;
uint8_t *ctrlbase = (uint8_t *)ccu->ccu_base;
struct shared_buf_map *sb_map_ptr =
(struct shared_buf_map *)(dmbase + MTK_CCU_SHARED_BUF_OFFSET);
ccu->ccu_ipc.ccuIntTrigPtr =
(uint32_t *)(ctrlbase + MTK_CCU_INT_TRG);
ccu->ccu_ipc.ccuIpcPtr =
(struct ap2ccu_ipc *)(dmbase + sb_map_ptr->ipc_base_offset);
ccu->ccu_ipc.ipcDataPtr = dmbase + sb_map_ptr->ipc_data_base_offset;
ccu->ccu_ipc.ipcDataAddrCcu = sb_map_ptr->ipc_data_addr_ccu;
ccu->ccu_ipc.ipcDataSize = sb_map_ptr->ipc_data_size;
if (ccu->ccu_ipc.ipcDataSize == 0)
ccu->ccu_ipc.ipcDataSize = MTK_CCU_IPC_IOBUF_CAPACITY;
ccu->ccu_ipc.is_initialized = true;
LOG_DBG_IPI("IPC max size %d bytes", ccu->ccu_ipc.ipcDataSize);
}
void mtk_ccu_rproc_ipc_uninit(struct mtk_ccu *ccu)
{
if (!ccu)
return;
ccu->ccu_ipc.is_initialized = false;
}
int mtk_ccu_ipc_register(struct platform_device *pdev,
enum mtk_ccu_to_ap_msg_id id,
mtk_ccu_ipc_handle_t handle, void *priv)
{
struct mtk_ccu *ccu = platform_get_drvdata(pdev);
if (!ccu) {
dev_err(&pdev->dev, "ccu device is not ready\n");
return -EPROBE_DEFER;
}
if (WARN_ON(id < 0) || WARN_ON(id >= MTK_CCU_MSG_TO_APMCU_MAX) ||
WARN_ON(handle == NULL))
return -EINVAL;
mutex_lock(&ccu->ipc_desc_lock);
ccu->ipc_desc[id].handler = handle;
ccu->ipc_desc[id].priv = priv;
mutex_unlock(&ccu->ipc_desc_lock);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_ccu_ipc_register);
void mtk_ccu_ipc_unregister(struct platform_device *pdev,
enum mtk_ccu_to_ap_msg_id id)
{
struct mtk_ccu *ccu = platform_get_drvdata(pdev);
if (!ccu)
return;
if (WARN_ON(id < 0) || WARN_ON(id >= MTK_CCU_MSG_TO_APMCU_MAX))
return;
mutex_lock(&ccu->ipc_desc_lock);
ccu->ipc_desc[id].handler = NULL;
ccu->ipc_desc[id].priv = NULL;
mutex_unlock(&ccu->ipc_desc_lock);
}
EXPORT_SYMBOL_GPL(mtk_ccu_ipc_unregister);
int mtk_ccu_rproc_ipc_send(struct platform_device *pdev,
enum mtk_ccu_feature_type featureType,
uint32_t msgId, void *inDataPtr, uint32_t inDataSize)
{
struct mtk_ccu_msg msg = {0};
uint32_t ret;
struct mtk_ccu *ccu;
if (!pdev)
return -EINVAL;
ccu = platform_get_drvdata(pdev);
if (!ccu) {
dev_err(&pdev->dev, "ccu device is not ready\n");
return -EPROBE_DEFER;
}
if (!ccu->ccu_ipc.is_initialized) {
dev_err(ccu->dev, "sendCcuCommnadIpc failed, ft (%d) msgId(%d)",
featureType, msgId);
return -EINVAL;
}
if (featureType == MTK_CCU_FEATURE_SYSCTRL)
LOG_DBG_IPI("[%u] ft(%d), msgId(%d)\n",
(uint32_t)arch_timer_read_counter(), featureType, msgId);
if ((inDataSize) && (!inDataPtr)) {
dev_err(ccu->dev, "inDataPtr is NULL\n");
return -EINVAL;
}
spin_lock(&ccu->ipc_send_lock);
//Check if need input data
ret = mtk_ccu_copyCmdInData(ccu, inDataPtr, inDataSize);
if (ret != 0) {
spin_unlock(&ccu->ipc_send_lock);
return -EINVAL;
}
msg.feature_type = featureType;
msg.msg_id = msgId;
msg.in_data_ptr = ccu->ccu_ipc.ipcDataAddrCcu;
msg.inDataSize = inDataSize;
ret = mtk_ccu_rproc_ipc_trigger(ccu, &msg);
if (ret != 0) {
dev_err(ccu->dev, "sendCcuCommnadIpc failed, msgId(%d)", msgId);
spin_unlock(&ccu->ipc_send_lock);
return -EINVAL;
}
//check if need to copy output data
ret = mtk_ccu_copyCmdOutData(ccu, inDataPtr, inDataSize);
spin_unlock(&ccu->ipc_send_lock);
/* LOG_DBG_IPI("-"); */
return ret;
}
EXPORT_SYMBOL_GPL(mtk_ccu_rproc_ipc_send);
int mtk_ccu_rproc_get_inforeg(struct platform_device *pdev,
uint32_t regno, uint32_t *data)
{
struct mtk_ccu *ccu;
if ((!pdev) || (!data))
return -EINVAL;
ccu = platform_get_drvdata(pdev);
if (!ccu)
return -EPROBE_DEFER;
if ((regno < 28) || (regno > 31))
return -EINVAL;
if (!spin_trylock(&ccu->ccu_poweron_lock))
return -EINVAL;
if (!ccu->poweron) {
spin_unlock(&ccu->ccu_poweron_lock);
return -EINVAL;
}
regno <<= 2;
*data = readl(ccu->ccu_base + MTK_CCU_SPARE_REG00 + regno);
spin_unlock(&ccu->ccu_poweron_lock);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_ccu_rproc_get_inforeg);
MODULE_DESCRIPTION("MTK CCU Rproc Driver");
MODULE_LICENSE("GPL v2");