654 lines
16 KiB
C
Executable file
654 lines
16 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 MediaTek Inc.
|
|
* Author: Anthony Huang <anthony.huang@mediatek.com>
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_domain.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/scmi_protocol.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/timer.h>
|
|
#include "cmdq-util.h"
|
|
#include "mtk-smi-dbg.h"
|
|
#include "tinysys-scmi.h"
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
|
|
#include <mt-plat/aee.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_MTK_SMI)
|
|
#include <soc/mediatek/smi.h>
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_DEVAPC)
|
|
#include <linux/soc/mediatek/devapc_public.h>
|
|
#endif
|
|
|
|
#define MMINFRA_MAX_CLK_NUM (4)
|
|
#define MAX_SMI_COMM_NUM (3)
|
|
|
|
struct mminfra_dbg {
|
|
void __iomem *ctrl_base;
|
|
void __iomem *mminfra_base;
|
|
void __iomem *gce_base;
|
|
ssize_t ctrl_size;
|
|
struct device *comm_dev[MAX_SMI_COMM_NUM];
|
|
struct notifier_block nb;
|
|
};
|
|
|
|
static struct notifier_block mtk_pd_notifier;
|
|
static struct scmi_tinysys_info_st *tinfo;
|
|
static int feature_id;
|
|
static struct clk *mminfra_clk[MMINFRA_MAX_CLK_NUM];
|
|
static atomic_t clk_ref_cnt = ATOMIC_INIT(0);
|
|
static struct device *dev;
|
|
static struct mminfra_dbg *dbg;
|
|
static u32 mminfra_bkrs;
|
|
static u32 bkrs_reg_pa;
|
|
|
|
|
|
#define MMINFRA_BASE 0x1e800000
|
|
#define GCE_BASE 0x1e980000
|
|
|
|
#define MMINFRA_CG_CON0 0x100
|
|
#define MMINFRA_DBG_SEL 0x300
|
|
#define MMINFRA_MODULE_DBG 0xf4
|
|
#define GCE_GCTL_VALUE 0x48
|
|
#define GCED_CG_BIT BIT(0)
|
|
#define GCEM_CG_BIT BIT(1)
|
|
#define SMI_CG_BIT BIT(2)
|
|
|
|
#define MMINFRA_CG_CON1 0x110
|
|
#define GCE26M_CG_BIT BIT(17)
|
|
|
|
#define GCED 0
|
|
#define GCEM 1
|
|
|
|
static bool mminfra_check_scmi_status(void)
|
|
{
|
|
if (tinfo)
|
|
return true;
|
|
|
|
tinfo = get_scmi_tinysys_info();
|
|
|
|
if (IS_ERR_OR_NULL(tinfo)) {
|
|
pr_notice("%s: tinfo is wrong!!\n", __func__);
|
|
tinfo = NULL;
|
|
return false;
|
|
}
|
|
|
|
if (IS_ERR_OR_NULL(tinfo->ph)) {
|
|
pr_notice("%s: tinfo->ph is wrong!!\n", __func__);
|
|
tinfo = NULL;
|
|
return false;
|
|
}
|
|
|
|
of_property_read_u32(tinfo->sdev->dev.of_node, "scmi-mminfra", &feature_id);
|
|
pr_notice("%s: get scmi_smi succeed id=%d!!\n", __func__, feature_id);
|
|
return true;
|
|
}
|
|
|
|
static void do_mminfra_bkrs(bool is_restore)
|
|
{
|
|
int err;
|
|
u64 start_ts, start_osts, end_ts, end_osts;
|
|
|
|
if (mminfra_check_scmi_status()) {
|
|
start_ts = sched_clock();
|
|
start_osts = __arch_counter_get_cntvct();
|
|
|
|
err = scmi_tinysys_common_set(tinfo->ph, feature_id,
|
|
2, (is_restore)?0:1, 0, 0, 0);
|
|
|
|
if (err) {
|
|
end_ts = sched_clock();
|
|
end_osts = __arch_counter_get_cntvct();
|
|
pr_notice("%s: call scmi(%d) err=%d osts:%llu ts:%llu\n",
|
|
__func__, is_restore, err, start_osts, start_ts);
|
|
if (err == -ETIMEDOUT) {
|
|
pr_notice("%s: call scmi(%d) timeout osts:%llu ts:%llu\n",
|
|
__func__, is_restore, end_osts, end_ts);
|
|
mdelay(3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct device *mminfra_get_if_in_use(void)
|
|
{
|
|
s32 i, ret = 0;
|
|
|
|
for (i = 0; i < MAX_SMI_COMM_NUM; i++) {
|
|
if (!dev || !dbg || !dbg->comm_dev[i])
|
|
break;
|
|
|
|
ret = pm_runtime_get_if_in_use(dbg->comm_dev[i]);
|
|
if (ret <= 0)
|
|
continue;
|
|
else
|
|
return dbg->comm_dev[i];
|
|
}
|
|
|
|
pr_info("MMinfra may off, idx:%d ret=%d\n", i, ret);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void mminfra_clk_set(bool is_enable)
|
|
{
|
|
int err = 0;
|
|
int i, j;
|
|
|
|
if (is_enable) {
|
|
for (i = 0; i < MMINFRA_MAX_CLK_NUM; i++) {
|
|
if (mminfra_clk[i])
|
|
err = clk_prepare_enable(mminfra_clk[i]);
|
|
else
|
|
break;
|
|
|
|
if (err) {
|
|
pr_notice("mminfra clk(%d) enable fail:%d\n", i, err);
|
|
for (j = i - 1; j >= 0; j--)
|
|
clk_disable_unprepare(mminfra_clk[j]);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = MMINFRA_MAX_CLK_NUM - 1; i >= 0; i--) {
|
|
if (mminfra_clk[i])
|
|
clk_disable_unprepare(mminfra_clk[i]);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool is_mminfra_power_on(void)
|
|
{
|
|
return (atomic_read(&clk_ref_cnt) > 0);
|
|
}
|
|
|
|
static bool is_gce_cg_on(u32 hw_id)
|
|
{
|
|
u32 con0_val;
|
|
|
|
con0_val = readl_relaxed(dbg->mminfra_base + MMINFRA_CG_CON0);
|
|
|
|
if (con0_val & (hw_id == GCED ? GCED_CG_BIT : GCEM_CG_BIT))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void mminfra_cg_check(bool on)
|
|
{
|
|
u32 con0_val;
|
|
u32 con1_val;
|
|
|
|
con0_val = readl_relaxed(dbg->mminfra_base + MMINFRA_CG_CON0);
|
|
con1_val = readl_relaxed(dbg->mminfra_base + MMINFRA_CG_CON1);
|
|
|
|
if (on) {
|
|
/* SMI CG still off */
|
|
if ((con0_val & (SMI_CG_BIT)) || (con0_val & GCEM_CG_BIT) ||
|
|
(con0_val & GCED_CG_BIT) || (con1_val & GCE26M_CG_BIT)) {
|
|
pr_notice("%s cg still off, CG_CON0:0x%x CG_CON1:0x%x\n",
|
|
__func__, con0_val, con1_val);
|
|
if (con0_val & (SMI_CG_BIT))
|
|
mtk_smi_dbg_cg_status();
|
|
if ((con0_val & GCEM_CG_BIT) || (con0_val & GCED_CG_BIT)
|
|
|| (con1_val & GCE26M_CG_BIT))
|
|
cmdq_dump_usage();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int mtk_mminfra_pd_callback(struct notifier_block *nb,
|
|
unsigned long flags, void *data)
|
|
{
|
|
int count;
|
|
void __iomem *test_base;
|
|
static u32 bk_val;
|
|
u32 val;
|
|
|
|
if (flags == GENPD_NOTIFY_ON) {
|
|
mminfra_clk_set(true);
|
|
mminfra_cg_check(true);
|
|
count = atomic_inc_return(&clk_ref_cnt);
|
|
if (mminfra_bkrs) {
|
|
/* avoid suspend/resume fail when mminfra debug
|
|
* is also initialized in sspm
|
|
*/
|
|
cmdq_util_mminfra_cmd(0);
|
|
cmdq_util_mminfra_cmd(3); //mminfra rfifo init
|
|
do_mminfra_bkrs(true);
|
|
}
|
|
test_base = ioremap(bkrs_reg_pa, 4);
|
|
val = readl_relaxed(test_base);
|
|
if (val != bk_val) {
|
|
pr_notice("%s: HRE restore failed %#x=%x\n",
|
|
__func__, bkrs_reg_pa, val);
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
|
|
aee_kernel_warning("mminfra",
|
|
"HRE restore failed %#x=%x, bk_val=%x\n",
|
|
bkrs_reg_pa, val, bk_val);
|
|
#endif
|
|
BUG_ON(1);
|
|
}
|
|
iounmap(test_base);
|
|
writel(0x20002, dbg->gce_base + GCE_GCTL_VALUE);
|
|
pr_notice("%s: enable clk ref_cnt=%d, enable gce apsrc: %#x=%#x\n",
|
|
__func__, count, GCE_BASE + GCE_GCTL_VALUE,
|
|
readl(dbg->gce_base + GCE_GCTL_VALUE));
|
|
} else if (flags == GENPD_NOTIFY_PRE_OFF) {
|
|
writel(0, dbg->gce_base + GCE_GCTL_VALUE);
|
|
pr_notice("%s: disable gce apsrc: %#x=%#x\n",
|
|
__func__, GCE_BASE + GCE_GCTL_VALUE, readl(dbg->gce_base + GCE_GCTL_VALUE));
|
|
test_base = ioremap(bkrs_reg_pa, 4);
|
|
bk_val = readl_relaxed(test_base);
|
|
if (mminfra_bkrs)
|
|
do_mminfra_bkrs(false);
|
|
iounmap(test_base); //Drv: add for prevent memory leak jinzhao 20240928
|
|
count = atomic_read(&clk_ref_cnt);
|
|
if (count != 1) {
|
|
pr_notice("%s: wrong clk ref_cnt=%d in PRE_OFF\n",
|
|
__func__, count);
|
|
return NOTIFY_OK;
|
|
}
|
|
mminfra_clk_set(false);
|
|
mminfra_cg_check(false);
|
|
count = atomic_dec_return(&clk_ref_cnt);
|
|
pr_notice("%s: disable clk ref_cnt=%d\n", __func__, count);
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
int mminfra_scmi_test(const char *val, const struct kernel_param *kp)
|
|
{
|
|
#ifdef MMINFRA_DEBUG
|
|
int ret, arg0;
|
|
unsigned int test_case;
|
|
void __iomem *test_base = ioremap(0x1e800280, 4);
|
|
|
|
ret = sscanf(val, "%u %d", &test_case, &arg0);
|
|
if (ret != 2) {
|
|
pr_notice("%s: invalid input: %s, result(%d)\n", __func__, val, ret);
|
|
return -EINVAL;
|
|
}
|
|
if (mminfra_check_scmi_status()) {
|
|
if (test_case == 2 && arg0 == 0) {
|
|
writel(0x123, test_base);
|
|
pr_notice("%s: before BKRS read 0x1e800280 = 0x%x\n",
|
|
__func__, readl_relaxed(test_base));
|
|
}
|
|
pr_notice("%s: feature_id=%d test_case=%d arg0=%d\n",
|
|
__func__, feature_id, test_case, arg0);
|
|
ret = scmi_tinysys_common_set(tinfo->ph, feature_id, test_case, arg0, 0, 0, 0);
|
|
pr_notice("%s: scmi return %d\n", __func__, ret);
|
|
if (test_case == 2 && arg0 == 1)
|
|
pr_notice("%s after BKRS read 0x1e800280 = 0x%x\n",
|
|
__func__, readl_relaxed(test_base));
|
|
}
|
|
|
|
iounmap(test_base);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static struct kernel_param_ops scmi_test_ops = {
|
|
.set = mminfra_scmi_test,
|
|
.get = param_get_int,
|
|
};
|
|
module_param_cb(scmi_test, &scmi_test_ops, NULL, 0644);
|
|
MODULE_PARM_DESC(scmi_test, "scmi test");
|
|
|
|
|
|
int mminfra_ut(const char *val, const struct kernel_param *kp)
|
|
{
|
|
#ifdef MMINFRA_DEBUG
|
|
int ret, arg0;
|
|
unsigned int test_case, value;
|
|
void __iomem *test_base;
|
|
|
|
ret = sscanf(val, "%u %i", &test_case, &arg0);
|
|
if (ret != 2) {
|
|
pr_notice("%s: invalid input: %s, result(%d)\n", __func__, val, ret);
|
|
return -EINVAL;
|
|
}
|
|
pr_notice("%s: input: %s\n", __func__, val);
|
|
switch (test_case) {
|
|
case 0:
|
|
ret = pm_runtime_get_sync(dev);
|
|
test_base = ioremap(arg0, 4);
|
|
value = readl_relaxed(test_base);
|
|
do_mminfra_bkrs(false); // backup
|
|
writel(0x123, test_base);
|
|
do_mminfra_bkrs(true); // restore
|
|
if (value == readl_relaxed(test_base))
|
|
pr_notice("%s: test_case(%d) pass\n",
|
|
__func__, test_case);
|
|
else
|
|
pr_notice("%s: test_case(%d) fail value=%d\n",
|
|
__func__, test_case, value);
|
|
iounmap(test_base);
|
|
pm_runtime_put_sync(dev);
|
|
break;
|
|
case 1:
|
|
ret = pm_runtime_get_sync(dev);
|
|
test_base = ioremap(bkrs_reg_pa, 4);
|
|
value = readl_relaxed(test_base);
|
|
do_mminfra_bkrs(false); // backup
|
|
writel(0x123, test_base);
|
|
do_mminfra_bkrs(true); // restore
|
|
if (value == readl_relaxed(test_base))
|
|
pr_notice("%s: test_case(%d) pass\n",
|
|
__func__, test_case);
|
|
else
|
|
pr_notice("%s: test_case(%d) fail value=%d\n",
|
|
__func__, test_case, value);
|
|
pr_notice("%s: HRE restore result %#x=%x value=%x\n",
|
|
__func__, bkrs_reg_pa, readl_relaxed(test_base), value);
|
|
iounmap(test_base);
|
|
pm_runtime_put_sync(dev);
|
|
break;
|
|
default:
|
|
pr_notice("%s: wrong test_case(%d)\n", __func__, test_case);
|
|
break;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static struct kernel_param_ops mminfra_ut_ops = {
|
|
.set = mminfra_ut,
|
|
.get = param_get_int,
|
|
};
|
|
module_param_cb(mminfra_ut, &mminfra_ut_ops, NULL, 0644);
|
|
MODULE_PARM_DESC(mminfra_ut, "mminfra ut");
|
|
|
|
#define MMINFRA_GALS_NR (6)
|
|
static void mminfra_gals_dump(void)
|
|
{
|
|
u32 i;
|
|
u32 mux_setting[MMINFRA_GALS_NR] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
|
|
|
|
for (i = 0; i < MMINFRA_GALS_NR; i++) {
|
|
writel(mux_setting[i], dbg->mminfra_base + MMINFRA_DBG_SEL);
|
|
pr_notice("%s: %#x=%#x, %#x=%#x\n", __func__,
|
|
MMINFRA_BASE + MMINFRA_DBG_SEL,
|
|
readl(dbg->mminfra_base + MMINFRA_DBG_SEL),
|
|
MMINFRA_BASE + MMINFRA_MODULE_DBG,
|
|
readl(dbg->mminfra_base + MMINFRA_MODULE_DBG));
|
|
}
|
|
|
|
}
|
|
|
|
int mtk_mminfra_dbg_hang_detect(const char *user)
|
|
{
|
|
s32 offset, len = 0, ret, i;
|
|
u32 val;
|
|
char buf[LINK_MAX + 1] = {0};
|
|
|
|
pr_info("%s: check caller:%s\n", __func__, user);
|
|
for (i = 0; i < MAX_SMI_COMM_NUM; i++) {
|
|
if (!dev || !dbg || !dbg->comm_dev[i])
|
|
return -ENODEV;
|
|
|
|
ret = pm_runtime_get_if_in_use(dbg->comm_dev[i]);
|
|
if (ret <= 0) {
|
|
dev_info(dev, " MMinfra may off, comm_nr(%d), %d\n", i, ret);
|
|
continue;
|
|
}
|
|
|
|
for (offset = 0; offset <= dbg->ctrl_size; offset += 4) {
|
|
val = readl_relaxed(dbg->ctrl_base + offset);
|
|
ret = snprintf(buf + len, LINK_MAX - len, " %#x=%#x,",
|
|
offset, val);
|
|
if (ret < 0 || ret >= LINK_MAX - len) {
|
|
ret = snprintf(buf + len, LINK_MAX - len, "%c", '\0');
|
|
if (ret < 0 || ret >= LINK_MAX - len)
|
|
pr_notice("%s: ret:%d buf size:%d\n",
|
|
__func__, ret, LINK_MAX - len);
|
|
dev_info(dev, "%s\n", buf);
|
|
|
|
len = 0;
|
|
memset(buf, '\0', sizeof(char) * ARRAY_SIZE(buf));
|
|
ret = snprintf(buf + len, LINK_MAX - len, " %#x=%#x,",
|
|
offset, val);
|
|
if (ret < 0 || ret >= LINK_MAX - len)
|
|
pr_notice("%s: ret:%d buf size:%d\n",
|
|
__func__, ret, LINK_MAX - len);
|
|
}
|
|
len += ret;
|
|
}
|
|
ret = snprintf(buf + len, LINK_MAX - len, "%c", '\0');
|
|
if (ret < 0 || ret >= LINK_MAX - len)
|
|
pr_notice("%s: ret:%d buf size:%d\n", __func__, ret, LINK_MAX - len);
|
|
dev_info(dev, "%s\n", buf);
|
|
|
|
mminfra_gals_dump();
|
|
pm_runtime_put(dbg->comm_dev[i]);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mminfra_smi_dbg_cb(struct notifier_block *nb,
|
|
unsigned long value, void *v)
|
|
{
|
|
mtk_mminfra_dbg_hang_detect("smi_dbg");
|
|
return 0;
|
|
}
|
|
|
|
static bool aee_dump;
|
|
static irqreturn_t mminfra_irq_handler(int irq, void *data)
|
|
{
|
|
struct device *comm_dev;
|
|
//char buf[LINK_MAX + 1] = {0};
|
|
|
|
pr_notice("handle mminfra irq!\n");
|
|
if (!dev || !dbg || !dbg->comm_dev[0])
|
|
return IRQ_NONE;
|
|
|
|
comm_dev = mminfra_get_if_in_use();
|
|
if (!comm_dev) {
|
|
pr_notice("%s: mminfra is power off\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
cmdq_util_mminfra_cmd(1);
|
|
|
|
if (!aee_dump) {
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
|
|
aee_kernel_warning("mminfra", "MMInfra bus timeout\n");
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_SMI)
|
|
mtk_smi_dbg_hang_detect("mminfra irq");
|
|
#endif
|
|
aee_dump = true;
|
|
}
|
|
|
|
cmdq_util_mminfra_cmd(0);
|
|
|
|
pm_runtime_put(comm_dev);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_DEVAPC)
|
|
static bool mminfra_devapc_power_cb(void)
|
|
{
|
|
return is_mminfra_power_on();
|
|
}
|
|
|
|
static struct devapc_power_callbacks devapc_power_handle = {
|
|
.type = DEVAPC_TYPE_MMINFRA,
|
|
.query_power = mminfra_devapc_power_cb,
|
|
};
|
|
#endif
|
|
|
|
static int mminfra_debug_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *node;
|
|
struct platform_device *comm_pdev;
|
|
struct property *prop;
|
|
struct resource *res;
|
|
const char *name;
|
|
struct clk *clk;
|
|
u32 comm_id;
|
|
int ret = 0, i = 0, irq, comm_nr = 0;
|
|
|
|
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
|
|
if (!dbg)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_notice(&pdev->dev, "could not get resource for ctrl\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dbg->ctrl_size = resource_size(res);
|
|
dbg->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(dbg->ctrl_base)) {
|
|
dev_notice(&pdev->dev, "could not ioremap resource for ctrl\n");
|
|
return PTR_ERR(dbg->ctrl_base);
|
|
}
|
|
|
|
for_each_compatible_node(node, NULL, "mediatek,smi-common") {
|
|
if (!node || !of_property_read_bool(node, "smi-common"))
|
|
continue;
|
|
|
|
of_property_read_u32(node, "mediatek,common-id", &comm_id);
|
|
comm_pdev = of_find_device_by_node(node);
|
|
of_node_put(node);
|
|
if (!comm_pdev)
|
|
return -EINVAL;
|
|
pr_notice("[mminfra] comm_id=%d, comm_nr=%d\n", comm_id, comm_nr);
|
|
dbg->comm_dev[comm_nr++] = &comm_pdev->dev;
|
|
}
|
|
dbg->nb.notifier_call = mminfra_smi_dbg_cb;
|
|
mtk_smi_dbg_register_notifier(&dbg->nb);
|
|
|
|
node = pdev->dev.of_node;
|
|
of_property_read_u32(node, "mminfra-bkrs", &mminfra_bkrs);
|
|
|
|
of_property_read_u32(node, "bkrs-reg", &bkrs_reg_pa);
|
|
if (!bkrs_reg_pa)
|
|
bkrs_reg_pa = MMINFRA_BASE + 0x280;
|
|
|
|
mminfra_check_scmi_status();
|
|
|
|
dev = &pdev->dev;
|
|
pm_runtime_enable(dev);
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
dev_notice(&pdev->dev, "failed to get irq (%d)\n", irq);
|
|
} else {
|
|
ret = devm_request_irq(&pdev->dev, irq, mminfra_irq_handler, IRQF_SHARED,
|
|
"mtk_mminfra_debug", dbg);
|
|
if (ret) {
|
|
dev_notice(&pdev->dev,
|
|
"failed to register ISR %d (%d)", irq, ret);
|
|
return ret;
|
|
}
|
|
cmdq_util_mminfra_cmd(0);
|
|
cmdq_util_mminfra_cmd(3); //mminfra rfifo init
|
|
}
|
|
|
|
dbg->mminfra_base = ioremap(MMINFRA_BASE, 0x8f4);
|
|
dbg->gce_base = ioremap(GCE_BASE, 0x1000);
|
|
|
|
cmdq_get_mminfra_cb(is_mminfra_power_on);
|
|
cmdq_get_mminfra_gce_cg_cb(is_gce_cg_on);
|
|
|
|
|
|
mtk_pd_notifier.notifier_call = mtk_mminfra_pd_callback;
|
|
ret = dev_pm_genpd_add_notifier(dev, &mtk_pd_notifier);
|
|
|
|
of_property_for_each_string(node, "clock-names", prop, name) {
|
|
clk = devm_clk_get(dev, name);
|
|
if (IS_ERR(clk)) {
|
|
dev_notice(dev, "%s: clks of %s init failed\n",
|
|
__func__, name);
|
|
ret = PTR_ERR(clk);
|
|
break;
|
|
}
|
|
|
|
if (i == MMINFRA_MAX_CLK_NUM) {
|
|
dev_notice(dev, "%s: clk num is wrong\n", __func__);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
mminfra_clk[i++] = clk;
|
|
}
|
|
if (of_property_read_bool(node, "init-clk-on")) {
|
|
atomic_inc(&clk_ref_cnt);
|
|
mminfra_clk_set(true);
|
|
pr_notice("%s: init-clk-on enable clk\n", __func__);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_DEVAPC)
|
|
register_devapc_power_callback(&devapc_power_handle);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mminfra_pm_suspend(struct device *dev)
|
|
{
|
|
mtk_smi_dbg_cg_status();
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops mminfra_debug_pm_ops = {
|
|
.suspend = mminfra_pm_suspend,
|
|
};
|
|
|
|
static const struct of_device_id of_mminfra_debug_match_tbl[] = {
|
|
{
|
|
.compatible = "mediatek,mminfra-debug",
|
|
},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver mminfra_debug_drv = {
|
|
.probe = mminfra_debug_probe,
|
|
.driver = {
|
|
.name = "mtk-mminfra-debug",
|
|
.of_match_table = of_mminfra_debug_match_tbl,
|
|
.pm = &mminfra_debug_pm_ops,
|
|
},
|
|
};
|
|
|
|
static int __init mtk_mminfra_debug_init(void)
|
|
{
|
|
s32 status;
|
|
|
|
status = platform_driver_register(&mminfra_debug_drv);
|
|
if (status) {
|
|
pr_notice("Failed to register MMInfra debug driver(%d)\n", status);
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void __exit mtk_mminfra_debug_exit(void)
|
|
{
|
|
platform_driver_unregister(&mminfra_debug_drv);
|
|
}
|
|
|
|
module_init(mtk_mminfra_debug_init);
|
|
module_exit(mtk_mminfra_debug_exit);
|
|
MODULE_DESCRIPTION("MTK MMInfra Debug driver");
|
|
MODULE_AUTHOR("Anthony Huang<anthony.huang@mediatek.com>");
|
|
MODULE_LICENSE("GPL v2");
|