476 lines
13 KiB
C
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");
|