6db4831e98
Android 14
485 lines
12 KiB
C
485 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <mt-plat/sync_write.h>
|
|
#include <mt-plat/mtk_io.h>
|
|
|
|
#include <dbgtop.h>
|
|
|
|
static void __iomem *DBGTOP_BASE;
|
|
static unsigned int LATCH_CTL2_OFFSET;
|
|
|
|
/*indicate have dbgtop hw*/
|
|
static int found_dbgtop_base;
|
|
|
|
static int mtk_dbgtop_probe(struct platform_device *pdev);
|
|
|
|
static int mtk_dbgtop_remove(struct platform_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id mtk_dbgtop_of_ids[] = {
|
|
{.compatible = "mediatek,dbgtop",},
|
|
{}
|
|
};
|
|
#endif
|
|
|
|
static struct platform_driver mtk_dbgtop = {
|
|
.probe = mtk_dbgtop_probe,
|
|
.remove = mtk_dbgtop_remove,
|
|
.driver = {
|
|
.name = "mtk_dbgtop",
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = mtk_dbgtop_of_ids,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int mtk_dbgtop_probe(struct platform_device *pdev)
|
|
{
|
|
struct resource *res;
|
|
|
|
if (DBGTOP_BASE) {
|
|
pr_info("%s: already got the base addr\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
DBGTOP_BASE = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(DBGTOP_BASE)) {
|
|
pr_info("[DBGTOP] unable to map DBGTOP_BASE");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t dbgtop_config_show(struct device_driver *driver, char *buf)
|
|
{
|
|
ssize_t ret = 0;
|
|
|
|
if (found_dbgtop_base) {
|
|
ret += snprintf(buf + ret, PAGE_SIZE - ret,
|
|
"%s,0x%x\n%s,0x%x\n%s,0x%x\n%s,0x%x\n",
|
|
"MTK_DBGTOP_MODE", readl(IOMEM(MTK_DBGTOP_MODE)),
|
|
"MTK_DBGTOP_LATCH_CTL", readl(IOMEM(MTK_DBGTOP_LATCH_CTL)),
|
|
"MTK_DBGTOP_DEBUG_CTL", readl(IOMEM(MTK_DBGTOP_DEBUG_CTL)),
|
|
"MTK_DBGTOP_DEBUG_CTL2", readl(IOMEM(MTK_DBGTOP_DEBUG_CTL2)));
|
|
} else {
|
|
ret += snprintf(buf + ret, PAGE_SIZE - ret,
|
|
"%s,0x%x\n%s,0x%x\n%s,0x%x\n%s,0x%x\n",
|
|
"MTK_DBGTOP_MODE", readl(IOMEM(MTK_DBGTOP_MODE)),
|
|
"MTK_DBGTOP_LATCH_CTL", readl(IOMEM(MTK_RGU_LATCH_CTL)),
|
|
"MTK_DBGTOP_DEBUG_CTL", readl(IOMEM(MTK_RGU_DEBUG_CTL)),
|
|
"MTK_DBGTOP_DEBUG_CTL2", readl(IOMEM(MTK_RGU_DEBUG_CTL2)));
|
|
}
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t dbgtop_config_store
|
|
(struct device_driver *driver, const char *buf, size_t count)
|
|
{
|
|
#if MTK_DBGTOP_TEST
|
|
char *ptr;
|
|
char *command;
|
|
|
|
if ((strlen(buf) + 1) > DBGTOP_MAX_CMD_LEN) {
|
|
pr_info("[DBGTOP] store command overflow\n");
|
|
return count;
|
|
}
|
|
|
|
command = kmalloc((size_t) DBGTOP_MAX_CMD_LEN, GFP_KERNEL);
|
|
if (!command)
|
|
return count;
|
|
strncpy(command, buf, (size_t) DBGTOP_MAX_CMD_LEN);
|
|
|
|
ptr = strsep(&command, " ");
|
|
|
|
if (!strncmp(buf, "0", strlen("0"))) {
|
|
mtk_dbgtop_dram_reserved(0);
|
|
mtk_dbgtop_cfg_dvfsrc(0);
|
|
mtk_dbgtop_pause_dvfsrc(0);
|
|
goto dbgtop_config_store;
|
|
} else if (!strncmp(buf, "1", strlen("1"))) {
|
|
mtk_dbgtop_dram_reserved(1);
|
|
mtk_dbgtop_cfg_dvfsrc(1);
|
|
mtk_dbgtop_pause_dvfsrc(1);
|
|
}
|
|
|
|
dbgtop_config_store:
|
|
kfree(command);
|
|
#endif
|
|
|
|
return count;
|
|
}
|
|
|
|
static DRIVER_ATTR_RW(dbgtop_config);
|
|
|
|
/*
|
|
* emi_ctrl_init: module init function.
|
|
*/
|
|
static int __init mtk_dbgtop_init(void)
|
|
{
|
|
int ret;
|
|
|
|
/* register DBGTOP interface */
|
|
ret = platform_driver_register(&mtk_dbgtop);
|
|
if (ret)
|
|
pr_info("[DBGTOP] fail to register mtk_dbgtop driver");
|
|
|
|
ret = driver_create_file(&mtk_dbgtop.driver,
|
|
&driver_attr_dbgtop_config);
|
|
if (ret)
|
|
pr_info("[DBGTOP] fail to create dbgtop_config");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* mtk_dbgtop_exit: module exit function.
|
|
*/
|
|
static void __exit mtk_dbgtop_exit(void)
|
|
{
|
|
}
|
|
|
|
int mtk_dbgtop_dram_reserved(int enable)
|
|
{
|
|
unsigned int tmp, ddr_reserve_mode;
|
|
|
|
if (DBGTOP_BASE == NULL)
|
|
return -1;
|
|
|
|
if (found_dbgtop_base)
|
|
ddr_reserve_mode = MTK_DBGTOP_MODE_DDR_RESERVE;
|
|
else
|
|
ddr_reserve_mode = MTK_RGU_MODE_DDR_RESERVE;
|
|
|
|
if (enable == 1) {
|
|
/* enable DDR reserved mode */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MODE));
|
|
tmp |= (ddr_reserve_mode | MTK_DBGTOP_MODE_KEY);
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MODE);
|
|
} else if (enable == 0) {
|
|
/* disable DDR reserved mode */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MODE));
|
|
tmp &= (~ddr_reserve_mode);
|
|
tmp |= MTK_DBGTOP_MODE_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MODE);
|
|
}
|
|
pr_info("%s: MTK_DBGTOP_MODE(0x%x)\n",
|
|
__func__, readl(IOMEM(MTK_DBGTOP_MODE)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_dram_reserved);
|
|
|
|
int mtk_dbgtop_cfg_dvfsrc(int enable)
|
|
{
|
|
unsigned int debug_ctl2, latch_ctl;
|
|
void __iomem *latch_ctl_base, *debug_ctl2_base;
|
|
|
|
if (DBGTOP_BASE == NULL)
|
|
return -1;
|
|
|
|
if (found_dbgtop_base) {
|
|
latch_ctl_base = MTK_DBGTOP_LATCH_CTL;
|
|
debug_ctl2_base = MTK_DBGTOP_DEBUG_CTL2;
|
|
} else {
|
|
latch_ctl_base = MTK_RGU_LATCH_CTL;
|
|
debug_ctl2_base = MTK_RGU_LATCH_CTL2;
|
|
}
|
|
|
|
debug_ctl2 = readl(IOMEM(debug_ctl2_base));
|
|
latch_ctl = readl(IOMEM(latch_ctl_base));
|
|
|
|
if (enable == 1) {
|
|
/* enable dvfsrc_en */
|
|
debug_ctl2 |= MTK_DBGTOP_DVFSRC_EN;
|
|
|
|
/* set dvfsrc_latch */
|
|
latch_ctl |= MTK_DBGTOP_DVFSRC_LATCH_EN;
|
|
} else {
|
|
/* disable is not allowed */
|
|
return -1;
|
|
}
|
|
|
|
debug_ctl2 |= MTK_DBGTOP_DEBUG_CTL2_KEY;
|
|
mt_reg_sync_writel(debug_ctl2, debug_ctl2_base);
|
|
|
|
latch_ctl |= MTK_DBGTOP_LATCH_CTL_KEY;
|
|
mt_reg_sync_writel(latch_ctl, latch_ctl_base);
|
|
|
|
pr_info("%s: MTK_DBGTOP_DEBUG_CTL2(0x%x)\n",
|
|
__func__, readl(IOMEM(debug_ctl2_base)));
|
|
pr_info("%s: MTK_DBGTOP_LATCH_CTL(0x%x)\n",
|
|
__func__, readl(IOMEM(latch_ctl_base)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_cfg_dvfsrc);
|
|
|
|
int mtk_dbgtop_pause_dvfsrc(int enable)
|
|
{
|
|
unsigned int tmp, dvfsrc_pause_pulse;
|
|
unsigned int count = 100;
|
|
void __iomem *debug_ctl_base, *debug_ctl2_base;
|
|
|
|
if (DBGTOP_BASE == NULL)
|
|
return -1;
|
|
if (found_dbgtop_base) {
|
|
dvfsrc_pause_pulse = MTK_DBGTOP_DVFSRC_PAUSE_PULSE;
|
|
debug_ctl_base = MTK_DBGTOP_DEBUG_CTL;
|
|
debug_ctl2_base = MTK_DBGTOP_DEBUG_CTL2;
|
|
} else {
|
|
#if defined(CONFIG_MACH_MT6779) || defined(CONFIG_MACH_MT6768) \
|
|
|| defined(CONFIG_MACH_MT6785) || defined(CONFIG_MACH_MT6781)
|
|
dvfsrc_pause_pulse = MTK_RGU_DVFSRC_PAUSE_PULSE;
|
|
debug_ctl_base = MTK_RGU_DEBUG_CTL;
|
|
debug_ctl2_base = MTK_RGU_DEBUG_CTL2;
|
|
#else
|
|
pr_info("%s: not support the function\n", __func__);
|
|
return -ENODEV;
|
|
#endif
|
|
}
|
|
if (!(readl(IOMEM(debug_ctl2_base))
|
|
& MTK_DBGTOP_DVFSRC_EN)) {
|
|
pr_info("%s: not enable DVFSRC\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (enable == 1) {
|
|
/* enable DVFSRC pause */
|
|
tmp = readl(IOMEM(debug_ctl_base));
|
|
tmp |= dvfsrc_pause_pulse;
|
|
tmp |= MTK_DBGTOP_DEBUG_CTL_KEY;
|
|
mt_reg_sync_writel(tmp, debug_ctl_base);
|
|
while (count--) {
|
|
if ((readl(IOMEM(debug_ctl_base))
|
|
& MTK_DBGTOP_DVFSRC_SUCECESS_ACK))
|
|
break;
|
|
udelay(10);
|
|
}
|
|
|
|
pr_info("%s: DVFSRC pause result(0x%x)\n",
|
|
__func__, readl(IOMEM(debug_ctl_base)));
|
|
} else if (enable == 0) {
|
|
/* disable DVFSRC pause */
|
|
tmp = readl(IOMEM(debug_ctl_base));
|
|
tmp &= (~dvfsrc_pause_pulse);
|
|
tmp |= MTK_DBGTOP_DEBUG_CTL_KEY;
|
|
mt_reg_sync_writel(tmp, debug_ctl_base);
|
|
}
|
|
|
|
pr_info("%s: MTK_DBGTOP_DEBUG_CTL(0x%x)\n",
|
|
__func__, readl(IOMEM(debug_ctl_base)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_pause_dvfsrc);
|
|
|
|
static int __init mtk_dbgtop_get_base_addr(void)
|
|
{
|
|
struct device_node *np_dbgtop;
|
|
|
|
for_each_matching_node(np_dbgtop, mtk_dbgtop_of_ids) {
|
|
pr_info("%s: compatible node found: %s\n",
|
|
__func__, np_dbgtop->name);
|
|
found_dbgtop_base = 1;
|
|
break;
|
|
}
|
|
|
|
if (!DBGTOP_BASE && found_dbgtop_base) {
|
|
DBGTOP_BASE = of_iomap(np_dbgtop, 0);
|
|
if (!DBGTOP_BASE)
|
|
pr_info("%s: dbgtop iomap failed\n", __func__);
|
|
} else if (!DBGTOP_BASE) {
|
|
np_dbgtop = of_find_compatible_node(NULL, NULL, "mediatek,toprgu");
|
|
DBGTOP_BASE = of_iomap(np_dbgtop, 0);
|
|
if (!DBGTOP_BASE)
|
|
pr_info("%s: dbgtop iomap failed\n", __func__);
|
|
pr_info("use toprgu to setting\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void get_dfd_base(void __iomem *dfd_base, unsigned int latch_offset)
|
|
{
|
|
LATCH_CTL2_OFFSET = latch_offset;
|
|
|
|
if (!DBGTOP_BASE)
|
|
pr_info("link RGU base failed.\n");
|
|
pr_info("Linked base: 0x%x\n", readl(IOMEM(DBGTOP_BASE)));
|
|
}
|
|
EXPORT_SYMBOL(get_dfd_base);
|
|
|
|
int mtk_dbgtop_dfd_count_en(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
/* dfd_count_en is obsolete, enable dfd_en only here */
|
|
if (value == 1) {
|
|
/* enable dfd_en */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp |= (MTK_DBGTOP_DFD_EN | MTK_DBGTOP_LATCH_CTL2_KEY);
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
} else if (value == 0) {
|
|
/* disable dfd_en */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp &= ~MTK_DBGTOP_DFD_EN;
|
|
tmp |= MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
#if defined(CONFIG_MACH_MT6781)
|
|
tmp = 0;
|
|
tmp |= MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_RGU_MFG_EN);
|
|
#endif
|
|
}
|
|
|
|
pr_info("%s: MTK_DBGTOP_LATCH_CTL2(0x%x)\n", __func__,
|
|
readl(IOMEM(MTK_DBGTOP_LATCH_CTL2)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_dfd_count_en);
|
|
|
|
int mtk_dbgtop_dfd_therm1_dis(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
if (value == 1) {
|
|
/* enable dfd count */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp |= MTK_DBGTOP_DFD_THERM1_DIS | MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
} else if (value == 0) {
|
|
/* disable dfd count */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp &= ~MTK_DBGTOP_DFD_THERM1_DIS;
|
|
tmp |= MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
}
|
|
|
|
pr_info("%s: MTK_DBGTOP_LATCH_CTL2(0x%x)\n", __func__,
|
|
readl(IOMEM(MTK_DBGTOP_LATCH_CTL2)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_dfd_therm1_dis);
|
|
|
|
int mtk_dbgtop_dfd_therm2_dis(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
if (value == 1) {
|
|
/* enable dfd count */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp |= MTK_DBGTOP_DFD_THERM2_DIS | MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
} else if (value == 0) {
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
tmp &= ~MTK_DBGTOP_DFD_THERM2_DIS;
|
|
tmp |= MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
}
|
|
|
|
pr_info("%s: MTK_DBGTOP_LATCH_CTL2(0x%x)\n", __func__,
|
|
readl(IOMEM(MTK_DBGTOP_LATCH_CTL2)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_dfd_therm2_dis);
|
|
|
|
int mtk_dbgtop_dfd_timeout(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
pr_debug("%s: before MTK_DBGTOP_LATCH_CTL2(0x%x)\n", __func__,
|
|
readl(IOMEM(MTK_DBGTOP_LATCH_CTL2)));
|
|
value <<= MTK_DBGTOP_DFD_TIMEOUT_SHIFT;
|
|
value &= MTK_DBGTOP_DFD_TIMEOUT_MASK;
|
|
/* break if dfd timeout >= target value */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_LATCH_CTL2));
|
|
|
|
/* set dfd timeout */
|
|
tmp &= ~MTK_DBGTOP_DFD_TIMEOUT_MASK;
|
|
tmp |= value | MTK_DBGTOP_LATCH_CTL2_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_LATCH_CTL2);
|
|
|
|
pr_debug("%s: MTK_DBGTOP_LATCH_CTL2(0x%x)\n", __func__,
|
|
readl(IOMEM(MTK_DBGTOP_LATCH_CTL2)));
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_dfd_timeout);
|
|
|
|
int mtk_dbgtop_mfg_pwr_on(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
if (!DBGTOP_BASE || !found_dbgtop_base)
|
|
return -1;
|
|
|
|
if (value == 1) {
|
|
/* set mfg pwr on */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MFG_REG));
|
|
tmp |= MTK_DBGTOP_MFG_PWR_ON;
|
|
tmp |= MTK_DBGTOP_MFG_REG_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MFG_REG);
|
|
} else if (value == 0) {
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MFG_REG));
|
|
tmp &= ~MTK_DBGTOP_MFG_PWR_ON;
|
|
tmp |= MTK_DBGTOP_MFG_REG_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MFG_REG);
|
|
} else
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_mfg_pwr_on);
|
|
|
|
int mtk_dbgtop_mfg_pwr_en(int value)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
if (!DBGTOP_BASE || !found_dbgtop_base)
|
|
return -1;
|
|
|
|
if (value == 1) {
|
|
/* set mfg pwr en */
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MFG_REG));
|
|
tmp |= MTK_DBGTOP_MFG_PWR_EN;
|
|
tmp |= MTK_DBGTOP_MFG_REG_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MFG_REG);
|
|
} else if (value == 0) {
|
|
tmp = readl(IOMEM(MTK_DBGTOP_MFG_REG));
|
|
tmp &= ~MTK_DBGTOP_MFG_PWR_EN;
|
|
tmp |= MTK_DBGTOP_MFG_REG_KEY;
|
|
mt_reg_sync_writel(tmp, MTK_DBGTOP_MFG_REG);
|
|
} else
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_dbgtop_mfg_pwr_en);
|
|
|
|
core_initcall(mtk_dbgtop_get_base_addr);
|
|
module_init(mtk_dbgtop_init);
|
|
module_exit(mtk_dbgtop_exit);
|
|
|