blob: c87e498c2421507bc0c83a2f823af33789c4ed86 [file]
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright (c) 2021 MediaTek Inc.
// Author: Qiqi Wang <qiqi.wang@mediatek.com>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/syscore_ops.h>
#include <dt-bindings/power/mt8696-power.h>
#include "clkchk.h"
#include "clkchk-mt8696.h"
/*
* clkchk dump_regs
*/
#define REGBASE_V(_phys, _id_name, _pg, _pn) { .phys = _phys, \
.name = #_id_name, .pg = _pg, .pn = _pn}
static struct regbase rb[] = {
[spm] = REGBASE_V(0x10006000, spm, PD_NULL, CLK_NULL),
[topckgen] = REGBASE_V(0x10000000, topckgen, PD_NULL, CLK_NULL),
[infracfg] = REGBASE_V(0x10001000, infracfg, PD_NULL, CLK_NULL),
[pericfg] = REGBASE_V(0x10003000, pericfg, PD_NULL, CLK_NULL),
[apmixedsys] = REGBASE_V(0x1000c000, apmixedsys, PD_NULL, CLK_NULL),
[fhctl] = REGBASE_V(0x10209e00, fhctl, PD_NULL, CLK_NULL),
[mcucfg] = REGBASE_V(0x0c530000, mcucfg, PD_NULL, CLK_NULL),
[ether] = REGBASE_V(0x1101a000, ether, MT8696_POWER_DOMAIN_ETHER_PCIE, CLK_NULL),
[mfgcfg] = REGBASE_V(0x13fbf000, mfgcfg, MT8696_POWER_DOMAIN_MFG, CLK_NULL),
[mmsys] = REGBASE_V(0x14000000, mmsys, MT8696_POWER_DOMAIN_MMSYS_COMMON, CLK_NULL),
[imgsys] = REGBASE_V(0x15000000, imgsys, MT8696_POWER_DOMAIN_DISP_MAIN, CLK_NULL),
[vdecsoc] = REGBASE_V(0x1600f000, vdecsoc, MT8696_POWER_DOMAIN_VDEC_SOC, CLK_NULL),
[vdeccore] = REGBASE_V(0x1602f000, vdeccore, MT8696_POWER_DOMAIN_VDEC_CORE0, CLK_NULL),
[vencsys] = REGBASE_V(0x18000000, vencsys, MT8696_POWER_DOMAIN_VENC, CLK_NULL),
{},
};
#define REGNAME(_base, _ofs, _name) \
{ .base = &rb[_base], .ofs = _ofs, .name = #_name }
static struct regname rn[] = {
REGNAME(topckgen, 0x010, CLK_CFG_0),
REGNAME(topckgen, 0x020, CLK_CFG_1),
REGNAME(topckgen, 0x030, CLK_CFG_2),
REGNAME(topckgen, 0x040, CLK_CFG_3),
REGNAME(topckgen, 0x050, CLK_CFG_4),
REGNAME(topckgen, 0x060, CLK_CFG_5),
REGNAME(topckgen, 0x070, CLK_CFG_6),
REGNAME(topckgen, 0x080, CLK_CFG_7),
REGNAME(topckgen, 0x090, CLK_CFG_8),
REGNAME(topckgen, 0x0a0, CLK_CFG_9),
REGNAME(topckgen, 0x0b0, CLK_CFG_10),
REGNAME(topckgen, 0x0c0, CLK_CFG_11),
REGNAME(topckgen, 0x0d0, CLK_CFG_12),
REGNAME(topckgen, 0x0e0, CLK_CFG_13),
REGNAME(topckgen, 0x0f0, CLK_CFG_14),
REGNAME(topckgen, 0x100, CLK_CFG_15),
REGNAME(topckgen, 0x110, CLK_CFG_16),
REGNAME(topckgen, 0x120, CLK_CFG_17),
REGNAME(topckgen, 0x338, CLK_AUDDIV_4),
REGNAME(topckgen, 0x328, CLK_AUDDIV_2),
REGNAME(topckgen, 0x334, CLK_AUDDIV_3),
REGNAME(topckgen, 0x320, CLK_AUDDIV_0),
REGNAME(topckgen, 0x338, CLK_AUDDIV_4),
REGNAME(topckgen, 0x170, CLK_MISC_CFG_0),
REGNAME(topckgen, 0x180, CLK_MISC_CFG_1),
REGNAME(mcucfg, 0xA2A0, CPU_PLLDIV_CFG0),
REGNAME(mcucfg, 0xA2E0, BUS_PLLDIV_CFG),
REGNAME(infracfg, 0x48, INFRA_PDN),
REGNAME(infracfg, 0x88, TRNG_PDN),
REGNAME(pericfg, 0x18, PERI_GLOBALCON_PDN0),
REGNAME(pericfg, 0x1c, PERI_GLOBALCON_PDN1),
REGNAME(pericfg, 0x042C, PERI_MSDC_CLK_EN),
REGNAME(apmixedsys, 0x012C, MAINPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0120, MAINPLL_CON0),
REGNAME(apmixedsys, 0x0124, MAINPLL_CON1),
REGNAME(apmixedsys, 0x0128, MAINPLL_CON2),
REGNAME(apmixedsys, 0x013C, UNIVPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0130, UNIVPLL_CON0),
REGNAME(apmixedsys, 0x0134, UNIVPLL_CON1),
REGNAME(apmixedsys, 0x0138, UNIVPLL_CON2),
REGNAME(apmixedsys, 0x019C, VDECPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0190, VDECPLL_CON0),
REGNAME(apmixedsys, 0x0194, VDECPLL_CON1),
REGNAME(apmixedsys, 0x0198, VDECPLL_CON2),
REGNAME(apmixedsys, 0x018C, ETHERPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0180, ETHERPLL_CON0),
REGNAME(apmixedsys, 0x0184, ETHERPLL_CON1),
REGNAME(apmixedsys, 0x0188, ETHERPLL_CON2),
REGNAME(apmixedsys, 0x020C, OSDPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0200, OSDPLL_CON0),
REGNAME(apmixedsys, 0x0204, OSDPLL_CON1),
REGNAME(apmixedsys, 0x0208, OSDPLL_CON2),
REGNAME(apmixedsys, 0x0220, APLL1_PWR_CON0),
REGNAME(apmixedsys, 0x0210, APLL1_CON0),
REGNAME(apmixedsys, 0x0214, APLL1_CON1),
REGNAME(apmixedsys, 0x0218, APLL1_CON2),
REGNAME(apmixedsys, 0x021c, APLL1_CON3),
REGNAME(apmixedsys, 0x0234, APLL2_PWR_CON0),
REGNAME(apmixedsys, 0x0224, APLL2_CON0),
REGNAME(apmixedsys, 0x0228, APLL2_CON1),
REGNAME(apmixedsys, 0x022c, APLL2_CON2),
REGNAME(apmixedsys, 0x0230, APLL2_CON3),
REGNAME(apmixedsys, 0x0248, APLL3_PWR_CON0),
REGNAME(apmixedsys, 0x0238, APLL3_CON0),
REGNAME(apmixedsys, 0x023C, APLL3_CON1),
REGNAME(apmixedsys, 0x0240, APLL3_CON2),
REGNAME(apmixedsys, 0x0244, APLL3_CON3),
REGNAME(apmixedsys, 0x025C, APLL4_PWR_CON0),
REGNAME(apmixedsys, 0x024C, APLL4_CON0),
REGNAME(apmixedsys, 0x0250, APLL4_CON1),
REGNAME(apmixedsys, 0x0254, APLL4_CON2),
REGNAME(apmixedsys, 0x0258, APLL4_CON3),
REGNAME(apmixedsys, 0x0270, APLL5_PWR_CON0),
REGNAME(apmixedsys, 0x0260, APLL5_CON0),
REGNAME(apmixedsys, 0x0264, APLL5_CON1),
REGNAME(apmixedsys, 0x0268, APLL5_CON2),
REGNAME(apmixedsys, 0x026c, APLL5_CON3),
REGNAME(apmixedsys, 0x0284, HDMIRX_APLL_PWR_CON0),
REGNAME(apmixedsys, 0x0274, HDMIRX_APLL_CON0),
REGNAME(apmixedsys, 0x0278, HDMIRX_APLL_CON1),
REGNAME(apmixedsys, 0x027c, HDMIRX_APLL_CON2),
REGNAME(apmixedsys, 0x0280, HDMIRX_APLL_CON3),
REGNAME(apmixedsys, 0x015C, MSDCPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0150, MSDCPLL_CON0),
REGNAME(apmixedsys, 0x0154, MSDCPLL_CON1),
REGNAME(apmixedsys, 0x0158, MSDCPLL_CON2),
REGNAME(apmixedsys, 0x017C, TVDPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0170, TVDPLL_CON0),
REGNAME(apmixedsys, 0x0174, TVDPLL_CON1),
REGNAME(apmixedsys, 0x0178, TVDPLL_CON2),
REGNAME(apmixedsys, 0x014C, MMPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0140, MMPLL_CON0),
REGNAME(apmixedsys, 0x0144, MMPLL_CON1),
REGNAME(apmixedsys, 0x0148, MMPLL_CON2),
REGNAME(apmixedsys, 0x011C, ARMPLL_PWR_CON0),
REGNAME(apmixedsys, 0x0110, ARMPLL_CON0),
REGNAME(apmixedsys, 0x0114, ARMPLL_CON1),
REGNAME(apmixedsys, 0x0118, ARMPLL_CON2),
REGNAME(apmixedsys, 0x02AC, CCIPLL_PWR_CON0),
REGNAME(apmixedsys, 0x02A0, CCIPLL_CON0),
REGNAME(apmixedsys, 0x2A4, CCIPLL_CON1),
REGNAME(apmixedsys, 0x2A8, CCIPLL_CON2),
REGNAME(ether, 0x300, ETHER_CKEN_0),
REGNAME(mfgcfg, 0x0, MFG_CG),
REGNAME(mmsys, 0x100, MMSYS_CG_0),
REGNAME(mmsys, 0x110, MMSYS_CG_1),
REGNAME(imgsys, 0xc, SYS_CFG_0C),
REGNAME(imgsys, 0x28, SYS_CFG_28),
REGNAME(vdecsoc, 0x8, LARB_CKEN_CON),
REGNAME(vdecsoc, 0x200, LAT_CKEN),
REGNAME(vdecsoc, 0x0, VDEC_CKEN),
REGNAME(vdeccore, 0x8, LARB_CKEN_CON),
REGNAME(vdeccore, 0x200, LAT_CKEN),
REGNAME(vdeccore, 0x0, VDEC_CKEN),
REGNAME(vencsys, 0x0, VENC_CG),
{},
};
static const struct regname *get_all_mt8696_regnames(void)
{
return rn;
}
static void init_regbase(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(rb) - 1; i++) {
if (!rb[i].phys)
continue;
rb[i].virt = ioremap(rb[i].phys, 0x1000);
}
}
/*
* clkchk pwr_status
*/
static u32 pwr_ofs[STA_NUM] = {
[PWR_STA] = 0x16C,
[PWR_STA2] = 0x170,
};
u32 *get_spm_pwr_status_array(void)
{
static void __iomem *scpsys_base, *pwr_addr[STA_NUM];
static u32 pwr_sta[STA_NUM];
int i;
for (i = 0; i < STA_NUM; i++) {
if (!scpsys_base)
scpsys_base = ioremap(0x10006000, PAGE_SIZE);
if (pwr_ofs[i]) {
pwr_addr[i] = scpsys_base + pwr_ofs[i];
pwr_sta[i] = clk_readl(pwr_addr[i]);
}
}
return pwr_sta;
}
/*
* clkchk pwr_state
*/
static struct pvd_msk pvd_pwr_mask[] = {
{"topckgen", PWR_STA, 0x00000000},
{"apmixedsys", PWR_STA, 0x00000000},
{"infracfg", PWR_STA, 0x00000000},
{"pericfg", PWR_STA, 0x00000000},
{"ether", PWR_STA, 0x00010000}, // BIT(16), ETHER_PCIE
{"mfgcfg", PWR_STA, 0x00100000}, // BIT(20), MFG
{"mmsys", PWR_STA, 0x00000008}, // BIT(3), MMSYS_COMMMON
{"imgsys", PWR_STA, 0x00000080}, // BIT(7), DISP_MAIN
{"vdecsoc", PWR_STA, 0x01000000}, // BIT(24), VDEC_SOC
{"vdeccore", PWR_STA, 0x00000001}, // BIT(0), VDEC_CORE0
{"vencsys", PWR_STA, 0x00000002}, // BIT(1), VENC
{},
};
static struct pvd_msk *get_pvd_pwr_mask(void)
{
return pvd_pwr_mask;
}
void print_subsys_reg_mt8696(enum chk_sys_id id)
{
struct regbase *rb_dump;
const struct regname *rns = &rn[0];
int i;
if (id >= chk_sys_num) {
pr_info("wrong id:%d\n", id);
return;
}
rb_dump = &rb[id];
for (i = 0; i < ARRAY_SIZE(rn) - 1; i++, rns++) {
if (!is_valid_reg(ADDR(rns)))
return;
/* filter out the subsys that we don't want */
if (rns->base != rb_dump)
continue;
pr_info("%-18s: [0x%08x] = 0x%08x\n",
rns->name, PHYSADDR(rns), clk_readl(ADDR(rns)));
}
}
EXPORT_SYMBOL(print_subsys_reg_mt8696);
/*
* clkchk dump_clks
*/
static const char * const off_pll_names[] = {
"vdecpll",
"etherpll",
"osdpll",
"apll1",
"apll2",
"apll3",
"apll4",
"apll5",
"hdmirx_apll",
"msdcpll",
"tvdpll",
"mmpll",
NULL
};
static const char * const *get_off_pll_names(void)
{
return off_pll_names;
}
static const char * const notice_off_pll_names[] = {
NULL
};
static const char * const *get_notice_pll_names(void)
{
return notice_off_pll_names;
}
/*
* init functions
*/
static struct clkchk_ops clkchk_mt8696_ops = {
.get_all_regnames = get_all_mt8696_regnames,
.get_spm_pwr_status_array = get_spm_pwr_status_array,
.get_pvd_pwr_mask = get_pvd_pwr_mask,
.get_off_pll_names = get_off_pll_names,
.get_notice_pll_names = get_notice_pll_names,
};
static int clk_chk_mt8696_probe(struct platform_device *pdev)
{
init_regbase();
set_clkchk_ops(&clkchk_mt8696_ops);
return 0;
}
static struct platform_driver clk_chk_mt8696_drv = {
.probe = clk_chk_mt8696_probe,
.driver = {
.name = "clk-chk-mt8696",
.owner = THIS_MODULE,
},
};
/*
* init functions
*/
static int __init clkchk_mt8696_init(void)
{
static struct platform_device *clk_chk_dev;
clk_chk_dev = platform_device_register_simple("clk-chk-mt8696", -1, NULL, 0);
if (IS_ERR(clk_chk_dev))
pr_info("unable to register clk-chk device");
register_syscore_ops(&clkchk_syscore_ops);
return platform_driver_register(&clk_chk_mt8696_drv);
}
static void __exit clkchk_mt8696_exit(void)
{
platform_driver_unregister(&clk_chk_mt8696_drv);
}
module_init(clkchk_mt8696_init);
module_exit(clkchk_mt8696_exit);
MODULE_LICENSE("GPL");