1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-10-07 10:09:34 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

293 lines
6.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/completion.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/sched/clock.h>
#include <linux/spinlock.h>
#include <linux/time64.h>
#include <linux/delay.h>
#include <mt-plat/aee.h>
#include "apu.h"
#include "apu_config.h"
#include "mtk_apu_rpmsg.h"
#include "apu_excep.h"
#include "apu_hw.h"
#include "hw_logger.h"
#include "apu_regdump.h"
#if IS_ENABLED(CONFIG_DEBUG_FS)
int apu_keep_awake;
#endif
/* cmd */
enum {
DPIDLE_CMD_LOCK_IPI = 0x5a00,
DPIDLE_CMD_UNLOCK_IPI = 0x5a01,
DPIDLE_CMD_PDN_UNLOCK = 0x5a02,
};
/* ack */
enum {
DPIDLE_ACK_OK = 0,
DPIDLE_ACK_LOCK_BUSY,
DPIDLE_ACK_POWER_DOWN_FAIL,
};
struct dpidle_msg {
uint32_t cmd;
uint32_t ack;
};
static struct mtk_apu *g_apu;
static struct work_struct pwron_dbg_wk;
static struct workqueue_struct *apu_deepidle_workq;
static struct dpidle_msg recv_msg;
static void apu_deepidle_pwron_dbg_fn(struct work_struct *work)
{
struct mtk_apu *apu = g_apu;
struct device *dev = apu->dev;
int i;
dev_info(dev, "mbox dummy= 0x%08x 0x%08x 0x%08x 0x%08x\n",
ioread32(apu->apu_mbox + 0x40),
ioread32(apu->apu_mbox + 0x44),
ioread32(apu->apu_mbox + 0x48),
ioread32(apu->apu_mbox + 0x4c));
usleep_range(0, 1000);
for (i = 0; i < 20; i++) {
dev_info(apu->dev, "apu boot: pc=%08x, sp=%08x\n",
ioread32(apu->md32_sysctrl + 0x838),
ioread32(apu->md32_sysctrl+0x840));
usleep_range(0, 1000);
}
dev_info(dev, "%s: UP_NORMAL_DOMAIN_NS = 0x%x\n",
__func__,
ioread32(apu->apu_sctrl_reviser + UP_NORMAL_DOMAIN_NS));
dev_info(dev, "%s: UP_PRI_DOMAIN_NS = 0x%x\n",
__func__,
ioread32(apu->apu_sctrl_reviser + UP_PRI_DOMAIN_NS));
dev_info(dev, "%s: USERFW_CTXT = 0x%x\n",
__func__,
ioread32(apu->apu_sctrl_reviser + USERFW_CTXT));
dev_info(dev, "%s: SECUREFW_CTXT = 0x%x\n",
__func__,
ioread32(apu->apu_sctrl_reviser + SECUREFW_CTXT));
dev_info(dev, "%s: MD32_SYS_CTRL = 0x%x\n",
__func__, ioread32(apu->md32_sysctrl + MD32_SYS_CTRL));
dev_info(dev, "%s: MD32_CLK_EN = 0x%x\n",
__func__, ioread32(apu->md32_sysctrl + MD32_CLK_EN));
dev_info(dev, "%s: UP_WAKE_HOST_MASK0 = 0x%x\n",
__func__, ioread32(apu->md32_sysctrl + UP_WAKE_HOST_MASK0));
dev_info(dev, "%s: MD32_BOOT_CTRL = 0x%x\n",
__func__, ioread32(apu->apu_ao_ctl + MD32_BOOT_CTRL));
dev_info(dev, "%s: MD32_PRE_DEFINE = 0x%x\n",
__func__, ioread32(apu->apu_ao_ctl + MD32_PRE_DEFINE));
}
int apu_deepidle_power_on_aputop(struct mtk_apu *apu)
{
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
struct device *dev = apu->dev;
struct timespec64 begin, end, delta;
uint32_t wait_ms = 10000;
int retry = 0;
int ret;
if (pm_runtime_suspended(apu->dev)) {
init_waitqueue_head(&apu->run.wq);
apu->run.signaled = 0;
if (!(apu->platdata->flags & F_SECURE_BOOT))
dev_info(apu->dev,
"%s: before warm boot pc=%08x, sp=%08x\n",
__func__,
ioread32(apu->md32_sysctrl + 0x838),
ioread32(apu->md32_sysctrl + 0x840));
apu->conf_buf->time_offset = sched_clock();
ret = hw_ops->power_on(apu);
if (ret == 0)
hw_logger_deep_idle_leave();
else
return ret;
if (!(apu->platdata->flags & F_SECURE_BOOT))
schedule_work(&pwron_dbg_wk);
ktime_get_ts64(&begin);
wait_for_warm_boot:
/* wait for remote warm boot done */
ret = wait_event_interruptible_timeout(apu->run.wq,
apu->run.signaled,
msecs_to_jiffies(wait_ms));
if (ret == -ERESTARTSYS) {
ktime_get_ts64(&end);
delta = timespec64_sub(end, begin);
if (delta.tv_sec > (wait_ms/1000)) {
dev_info(dev, "%s: retry(%d) over %u seconds!\n",
__func__, retry, (wait_ms/1000));
dev_info(dev, "APU warm boot timeout!!\n");
apu_regdump();
apusys_rv_aee_warn("APUSYS_RV",
"APUSYS_RV_TIMEOUT");
return -1;
}
if (retry % 50 == 0)
dev_info(dev,
"%s: wait APU interrupted by a signal, retry again\n",
__func__);
retry++;
msleep(20);
goto wait_for_warm_boot;
}
if (ret == 0) {
dev_info(dev, "APU warm boot timeout!!\n");
apu_regdump();
apusys_rv_aee_warn("APUSYS_RV",
"APUSYS_RV_TIMEOUT");
return -1;
}
dev_info(apu->dev, "%s: warm boot done\n", __func__);
}
return 0;
}
static int apu_deepidle_send_ack(struct mtk_apu *apu, uint32_t cmd, uint32_t ack)
{
struct dpidle_msg msg;
int ret;
msg.cmd = cmd;
msg.ack = ack;
ret = apu_ipi_send(apu, APU_IPI_DEEP_IDLE, &msg, sizeof(msg), 0);
if (ret)
dev_info(apu->dev,
"%s: failed to send ack msg, ack=%d, ret=%d\n",
__func__, ack, ret);
return ret;
}
static void apu_deepidle_work_func(struct work_struct *work)
{
struct mtk_apu *apu = container_of(work, struct mtk_apu, deepidle_work);
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
struct dpidle_msg *msg = &recv_msg;
int ret;
switch (msg->cmd) {
case DPIDLE_CMD_LOCK_IPI:
ret = apu_ipi_lock(apu);
if (ret) {
dev_info(apu->dev, "%s: IPI busy, ret=%d\n",
__func__, ret);
apu_deepidle_send_ack(apu, DPIDLE_CMD_LOCK_IPI,
DPIDLE_ACK_LOCK_BUSY);
return;
}
apu_deepidle_send_ack(apu, DPIDLE_CMD_LOCK_IPI,
DPIDLE_ACK_OK);
break;
case DPIDLE_CMD_UNLOCK_IPI:
apu_ipi_unlock(apu);
apu_deepidle_send_ack(apu, DPIDLE_CMD_UNLOCK_IPI,
DPIDLE_ACK_OK);
break;
case DPIDLE_CMD_PDN_UNLOCK:
hw_logger_deep_idle_enter_pre();
apu_deepidle_send_ack(apu, DPIDLE_CMD_PDN_UNLOCK,
DPIDLE_ACK_OK);
ret = hw_ops->power_off(apu);
if (ret) {
dev_info(apu->dev, "failed to power off ret=%d\n", ret);
hw_logger_deep_idle_enter_post();
apu_ipi_unlock(apu);
WARN_ON(0);
return;
}
hw_logger_deep_idle_enter_post();
apu_ipi_unlock(apu);
dev_info(apu->dev, "power off done\n");
break;
default:
dev_info(apu->dev, "unknown cmd %x\n", msg->cmd);
break;
}
}
static void apu_deepidle_ipi_handler(void *data, unsigned int len, void *priv)
{
struct mtk_apu *apu = (struct mtk_apu *)priv;
memcpy(&recv_msg, data, len);
queue_work(apu_deepidle_workq, &apu->deepidle_work);
}
int apu_deepidle_init(struct mtk_apu *apu)
{
struct device *dev = apu->dev;
int ret;
apu_deepidle_workq = alloc_workqueue("apu_deepidle",
WQ_UNBOUND | WQ_HIGHPRI, 0);
if (!apu_deepidle_workq) {
dev_info(apu->dev, "%s: failed to allocate rq for deep idle\n",
__func__);
return -ENOMEM;
}
INIT_WORK(&apu->deepidle_work, apu_deepidle_work_func);
ret = apu_ipi_register(apu, APU_IPI_DEEP_IDLE,
apu_deepidle_ipi_handler, apu);
if (ret) {
dev_info(dev,
"%s: failed to register deepidle ipi, ret=%d\n",
__func__, ret);
}
g_apu = apu;
INIT_WORK(&pwron_dbg_wk, apu_deepidle_pwron_dbg_fn);
return ret;
}
void apu_deepidle_exit(struct mtk_apu *apu)
{
/* module can be removed only after APU TOP is shutdown properly */
flush_work(&pwron_dbg_wk);
apu_ipi_unregister(apu, APU_IPI_DEEP_IDLE);
if (apu_deepidle_workq)
destroy_workqueue(apu_deepidle_workq);
}