// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. * Author: Owen Chen */ #include #include #include #include #include #define TAG "[clkchk] " #define BUG_ON_CHK_ENABLE 1 static const char * const *get_all_clk_names(void) { static const char * const clks[] = { /* PLLs */ "armpll", "armpll_l", "ccipll", "mainpll", "univpll", "msdcpll", "mfgpll", "mmpll", "mpll", "apll1", /* TOP */ "syspll_ck", "syspll_d2", "syspll1_d2", "syspll1_d4", "syspll1_d8", "syspll1_d16", "syspll_d3", "syspll2_d2", "syspll2_d4", "syspll2_d8", "syspll_d5", "syspll3_d2", "syspll3_d4", "syspll_d7", "syspll4_d2", "syspll4_d4", "usb20_192m_ck", "usb20_192m_d4", "usb20_192m_d8", "usb20_192m_d16", "usb20_192m_d32", "univpll_d2", "univpll1_d2", "univpll1_d4", "univpll_d3", "univpll2_d2", "univpll2_d4", "univpll2_d8", "univpll2_d32", "univpll_d5", "univpll3_d2", "univpll3_d4", "mmpll_ck", "mmpll_d2", "mpll_ck", "mpll_104m_div_ck", "mpll_52m_div_ck", "mfgpll_ck", "msdcpll_ck", "msdcpll_d2", "apll1_ck", "apll1_d2", "apll1_d4", "apll1_d8", "ulposc1_ck", "ulposc1_d2", "ulposc1_d4", "ulposc1_d8", "ulposc1_d16", "ulposc1_d32", "f_frtc_ck", "clk_26m_ck", "dmpll_ck", "axi_sel", "mem_sel", "mm_sel", "scp_sel", "mfg_sel", "atb_sel", "camtg_sel", "camtg1_sel", "camtg2_sel", "camtg3_sel", "uart_sel", "spi_sel", "msdc50_hclk_sel", "msdc50_0_sel", "msdc30_1_sel", "audio_sel", "aud_intbus_sel", "aud_1_sel", "aud_engen1_sel", "disp_pwm_sel", "sspm_sel", "dxcc_sel", "usb_top_sel", "spm_sel", "i2c_sel", "pwm_sel", "seninf_sel", "aes_fde_sel", "pwrap_ulposc_sel", "camtm_sel", /* INFRACFG */ "ifr_axi_dis", "ifr_pmic_tmr", "ifr_pmic_ap", "ifr_pmic_md", "ifr_pmic_conn", "ifr_scp_core", "ifr_sej", "ifr_apxgpt", "ifr_icusb", "ifr_gce", "ifr_therm", "ifr_i2c_ap", "ifr_i2c_ccu", "ifr_i2c_sspm", "ifr_i2c_rsv", "ifr_pwm_hclk", "ifr_pwm1", "ifr_pwm2", "ifr_pwm3", "ifr_pwm4", "ifr_pwm5", "ifr_pwm", "ifr_uart0", "ifr_uart1", "ifr_gce_26m", "ifr_dma", "ifr_btif", "ifr_spi0", "ifr_msdc0", "ifr_msdc1", "ifr_dvfsrc", "ifr_gcpu", "ifr_trng", "ifr_auxadc", "ifr_cpum", "ifr_ccif1_ap", "ifr_ccif1_md", "ifr_auxadc_md", "ifr_ap_dma", "ifr_xiu", "ifr_dapc", "ifr_ccif_ap", "ifr_debugtop", "ifr_audio", "ifr_ccif_md", "ifr_secore", "ifr_dxcc_ao", "ifr_dramc26", "ifr_pwmfb", "ifr_disp_pwm", "ifr_cldmabclk", "ifr_audio26m", "ifr_spi1", "ifr_i2c4", "ifr_mdtemp", "ifr_spi2", "ifr_spi3", "ifr_hf_fsspm", "ifr_i2c5", "ifr_i2c5a", "ifr_i2c5_imm", "ifr_i2c1a", "ifr_i2c1_imm", "ifr_i2c2a", "ifr_i2c2_imm", "ifr_spi4", "ifr_spi5", "ifr_cq_dma", "ifr_faes_fde_ck", "ifr_msdc0f", "ifr_msdc1sf", "ifr_sspm_26m", "ifr_sspm_32k", "ifr_i2c6", "ifr_ap_msdc0", "ifr_md_msdc0", "ifr_msdc0_clk", "ifr_msdc1_clk", "ifr_sej_f13m", "ifr_aes", "ifr_mcu_pm_bclk", "ifr_ccif2_ap", "ifr_ccif2_md", "ifr_ccif3_ap", "ifr_ccif3_md", /* PERICFG */ "periaxi_disable", /* AUDIO */ "aud_afe", "aud_22m", "aud_apll_tuner", "aud_adc", "aud_dac", "aud_dac_predis", "aud_tml", "aud_i2s1_bclk", "aud_i2s2_bclk", "aud_i2s3_bclk", "aud_i2s4_bclk", /* CAM */ "cam_larb3", "cam_dfp_vad", "cam", "camtg", "cam_seninf", "camsv0", "camsv1", "camsv2", "cam_ccu", /* IMG */ "img_larb2", "img_dip", "img_fdvt", "img_dpe", "img_rsc", /* MFG */ "mfgcfg_baxi", "mfgcfg_bmem", "mfgcfg_bg3d", "mfgcfg_b26m", /* MM */ "mm_mdp_rdma0", "mm_mdp_ccorr0", "mm_mdp_rsz0", "mm_mdp_rsz1", "mm_mdp_tdshp0", "mm_mdp_wrot0", "mm_mdp_wdma0", "mm_disp_ovl0", "mm_disp_ovl0_2l", "mm_disp_rsz", "mm_disp_rdma0", "mm_disp_wdma0", "mm_disp_color0", "mm_disp_ccorr0", "mm_disp_aal0", "mm_disp_gamma0", "mm_disp_dither0", "mm_dsi0", "mm_fake_eng", "mm_smi_common", "mm_smi_larb0", "mm_smi_comm0", "mm_smi_comm1", "mm_cam_mdp_ck", "mm_smi_img_ck", "mm_smi_cam_ck", "mm_img_dl_relay", "mm_imgdl_async", "mm_dig_dsi_ck", "mm_hrtwt", /* VENC */ "venc_set0_larb", "venc_set1_venc", "jpgenc", "venc_set3_vdec", /* MIPI */ "mipi0a_csr_0a", "mipi0b_csr_0b", "mipi1a_csr_1a", "mipi1b_csr_1b", "mipi2a_csr_2a", "mipi2b_csr_2b", /* GCE */ "gce", /* APMIXED 26MCK */ "apmixed_ssusb26m", "apmixed_appll26m", "apmixed_mipic026m", "apmixed_mdpll26m", "apmixed_mmsys26m", "apmixed_ufs26m", "apmixed_mipic126m", "apmixed_mempll26m", "apmixed_lvpll26m", "apmixed_mipid026m", /* end */ NULL }; return clks; } static const char * const off_pll_names[] = { "univ2pll", "apll1", "mfgpll", "msdcpll", "mmpll", NULL }; static const char * const notice_pll_names[] = { NULL }; static const char * const off_mtcmos_names[] = { "pg_dis", "pg_mfg", "pg_isp", "pg_mfg_core0", "pg_mfg_async", "pg_cam", "pg_vcodec", NULL }; static const char * const notice_mtcmos_names[] = { "pg_md1", "pg_conn", NULL }; static const char *ccf_state(struct clk_hw *hw) { if (__clk_get_enable_count(hw->clk)) return "enabled"; if (clk_hw_is_prepared(hw)) return "prepared"; return "disabled"; } static void print_enabled_clks(void) { const char * const *cn = get_all_clk_names(); const char *fix_clk = "clk26m"; for (; *cn; cn++) { int valid = 0; struct clk *c = __clk_lookup(*cn); struct clk_hw *c_hw = __clk_get_hw(c); struct clk_hw *p_hw; const char *c_name; const char *p_name; const char * const *pn; if (IS_ERR_OR_NULL(c) || !c_hw) continue; if (!__clk_get_enable_count(c)) continue; p_hw = clk_hw_get_parent(c_hw); c_name = clk_hw_get_name(c_hw); p_name = p_hw ? clk_hw_get_name(p_hw) : 0; while (p_name && strcmp(p_name, fix_clk)) { struct clk_hw *p_hw_temp; p_hw_temp = clk_hw_get_parent(p_hw); p_name = p_hw_temp ? clk_hw_get_name(p_hw_temp) : 0; if (p_name && strcmp(p_name, fix_clk)) p_hw = p_hw_temp; else if (p_name && !strcmp(p_name, fix_clk)) { c_name = clk_hw_get_name(p_hw); break; } } for (pn = off_pll_names; *pn && c_name; pn++) if (!strncmp(c_name, *pn, 10)) { valid++; break; } if (!valid) continue; p_hw = clk_hw_get_parent(c_hw); pr_notice("[%-17s: %8s, %3d, %3d, %10ld, %17s]\n", clk_hw_get_name(c_hw), ccf_state(c_hw), clk_hw_is_prepared(c_hw), __clk_get_enable_count(c), clk_hw_get_rate(c_hw), p_hw ? clk_hw_get_name(p_hw) : "- "); } } static void check_pll_off(void) { static struct clk *off_plls[ARRAY_SIZE(off_pll_names)]; struct clk **c; int invalid = 0; if (!off_plls[0]) { const char * const *pn; for (pn = off_pll_names, c = off_plls; *pn; pn++, c++) *c = __clk_lookup(*pn); } for (c = off_plls; *c; c++) { struct clk_hw *c_hw = __clk_get_hw(*c); if (!c_hw) continue; if (!clk_hw_is_enabled(c_hw)) continue; pr_notice("suspend warning[0m: %s is on\n", clk_hw_get_name(c_hw)); invalid++; } if (invalid) { print_enabled_clks(); #ifdef CONFIG_MTK_ENG_BUILD #if BUG_ON_CHK_ENABLE BUG_ON(1); #else aee_kernel_warning("CCF MT6765", "@%s():%d, PLLs are not off\n", __func__, __LINE__); WARN_ON(1); #endif #else aee_kernel_warning("CCF MT6765", "@%s():%d, PLLs are not off\n", __func__, __LINE__); WARN_ON(1); #endif } } static void check_pll_notice(void) { static struct clk *off_plls[ARRAY_SIZE(notice_pll_names)]; struct clk **c; int invalid = 0; if (!off_plls[0]) { const char * const *pn; for (pn = notice_pll_names, c = off_plls; *pn; pn++, c++) *c = __clk_lookup(*pn); } for (c = off_plls; *c; c++) { struct clk_hw *c_hw = __clk_get_hw(*c); if (!c_hw) continue; if (!clk_hw_is_enabled(c_hw)) continue; pr_notice("suspend warning[0m: %s is on\n", clk_hw_get_name(c_hw)); invalid++; } if (invalid) print_enabled_clks(); } static void check_mtcmos_off(void) { static struct clk *off_mtcmos[ARRAY_SIZE(off_mtcmos_names)]; struct clk **c; int invalid = 0; if (!off_mtcmos[0]) { const char * const *pn; for (pn = off_mtcmos_names, c = off_mtcmos; *pn; pn++, c++) *c = __clk_lookup(*pn); } for (c = off_mtcmos; *c; c++) { struct clk_hw *c_hw = __clk_get_hw(*c); if (!c_hw) continue; if (!clk_hw_is_prepared(c_hw) && !clk_hw_is_enabled(c_hw)) continue; pr_notice("suspend warning[0m: %s is on\n", clk_hw_get_name(c_hw)); invalid++; } if (invalid) { #ifdef CONFIG_MTK_ENG_BUILD #if BUG_ON_CHK_ENABLE BUG_ON(1); #else aee_kernel_warning("CCF MT6765", "@%s():%d, MTCMOSs are not off\n", __func__, __LINE__); WARN_ON(1); #endif #else aee_kernel_warning("CCF MT6765", "@%s():%d, MTCMOSs are not off\n", __func__, __LINE__); WARN_ON(1); #endif } } static void check_mtcmos_notice(void) { static struct clk *notice_mtcmos[ARRAY_SIZE(notice_mtcmos_names)]; struct clk **c; if (!notice_mtcmos[0]) { const char * const *pn; for (pn = notice_mtcmos_names, c = notice_mtcmos; *pn; pn++, c++) *c = __clk_lookup(*pn); } for (c = notice_mtcmos; *c; c++) { struct clk_hw *c_hw = __clk_get_hw(*c); if (!c_hw) continue; if (!clk_hw_is_prepared(c_hw) && !clk_hw_is_enabled(c_hw)) continue; pr_notice("suspend warning[0m: %s\n", clk_hw_get_name(c_hw)); } } void print_enabled_clks_once(void) { static bool first_flag = true; if (first_flag) { first_flag = false; print_enabled_clks(); } } EXPORT_SYMBOL(print_enabled_clks_once); static int clkchk_syscore_suspend(void) { check_pll_off(); check_pll_notice(); check_mtcmos_notice(); check_mtcmos_off(); return 0; } static void clkchk_syscore_resume(void) { } static struct syscore_ops clkchk_syscore_ops = { .suspend = clkchk_syscore_suspend, .resume = clkchk_syscore_resume, }; static int __init clkchk_mt6765_init(void) { if (!of_machine_is_compatible("mediatek,MT6765")) return -ENODEV; register_syscore_ops(&clkchk_syscore_ops); return 0; } static void __exit clkchk_mt6765_exit(void) { } subsys_initcall(clkchk_mt6765_init); module_exit(clkchk_mt6765_exit); MODULE_LICENSE("GPL");