// SPDX-License-Identifier: GPL-2.0 // // Copyright (c) 2019 MediaTek Inc. #include #include #include #include #include #include #include #include #include #define NOTIFY_TIMES_MAX 2 #define PMIC_OC_DEBUG_DUMP(_pmic_reg) \ { \ regmap_read(regmap, _pmic_reg, &val); \ pr_notice(#_pmic_reg"=0x%x\n", val); \ } struct reg_oc_debug_t { const char *name; struct notifier_block nb; unsigned int times; unsigned int md_reg_idx; bool is_md_reg; }; enum { MD_REG_OC_0, MD_REG_OC_1, MD_REG_OC_2, MD_REG_OC_3, MD_REG_OC_4, MD_REG_OC_5, }; #define REG_OC_DEBUG(_name) \ { \ .name = #_name, \ } #define MD_REG_OC_DEBUG(_name, _idx) \ { \ .name = #_name, \ .is_md_reg = true, \ .md_reg_idx = _idx, \ } static struct regmap *regmap; static struct reg_oc_debug_t mt6357_reg_oc_debug[] = { REG_OC_DEBUG(vproc), REG_OC_DEBUG(vcore), REG_OC_DEBUG(vmodem), REG_OC_DEBUG(vs1), MD_REG_OC_DEBUG(vpa, MD_REG_OC_0), REG_OC_DEBUG(vcore_pr), MD_REG_OC_DEBUG(vfe28, MD_REG_OC_1), REG_OC_DEBUG(vxo22), MD_REG_OC_DEBUG(vrf18, MD_REG_OC_2), MD_REG_OC_DEBUG(vrf12, MD_REG_OC_3), REG_OC_DEBUG(vefuse), /*REG_OC_DEBUG(vcn33_bt),*/ /*REG_OC_DEBUG(vcn33_wifi),*/ REG_OC_DEBUG(vcn28), REG_OC_DEBUG(vcn18), /*REG_OC_DEBUG(vcama),*/ /*REG_OC_DEBUG(vcamd),*/ /*REG_OC_DEBUG(vcamio),*/ /*REG_OC_DEBUG(vldo28),*/ REG_OC_DEBUG(vusb33), REG_OC_DEBUG(vaux18), REG_OC_DEBUG(vaud28), REG_OC_DEBUG(vio28), REG_OC_DEBUG(vio18), REG_OC_DEBUG(vsram_proc), REG_OC_DEBUG(vsram_others), /*REG_OC_DEBUG(vibr),*/ REG_OC_DEBUG(vdram), /*REG_OC_DEBUG(vmc),*/ /*REG_OC_DEBUG(vmch),*/ REG_OC_DEBUG(vemc), /*REG_OC_DEBUG(vsim1),*/ /*REG_OC_DEBUG(vsim2),*/ }; static struct reg_oc_debug_t mt6359_reg_oc_debug[] = { REG_OC_DEBUG(vs1), REG_OC_DEBUG(vgpu11), REG_OC_DEBUG(vmodem), REG_OC_DEBUG(vpu), REG_OC_DEBUG(vcore), REG_OC_DEBUG(vs2), MD_REG_OC_DEBUG(vpa, MD_REG_OC_0), REG_OC_DEBUG(vproc2), REG_OC_DEBUG(vproc1), //vcore_sshub REG_OC_DEBUG(vaud18), //REG_OC_DEBUG(vsim1), camera REG_OC_DEBUG(vibr), MD_REG_OC_DEBUG(vrf12, MD_REG_OC_3), REG_OC_DEBUG(vusb), REG_OC_DEBUG(vsram_proc2), REG_OC_DEBUG(vio18), //REG_OC_DEBUG(vcamio), camera REG_OC_DEBUG(vcn18), MD_REG_OC_DEBUG(vfe28, MD_REG_OC_1), REG_OC_DEBUG(vcn13), //vcn33_1_bt //vcn33_1_wifi REG_OC_DEBUG(vaux18), REG_OC_DEBUG(vsram_others), REG_OC_DEBUG(vefuse), //vxo22 REG_OC_DEBUG(vrfck), REG_OC_DEBUG(vbif28), REG_OC_DEBUG(vio28), REG_OC_DEBUG(vemc), //vcn33_2_bt //vcn33_2_wifi REG_OC_DEBUG(va12), REG_OC_DEBUG(va09), MD_REG_OC_DEBUG(vrf18, MD_REG_OC_2), REG_OC_DEBUG(vsram_md), REG_OC_DEBUG(vufs), REG_OC_DEBUG(vm18), REG_OC_DEBUG(vbbck), REG_OC_DEBUG(vsram_proc1), //REG_OC_DEBUG(vsim2), camera //vsram_others_sshub }; static int md_reg_oc_notify(struct reg_oc_debug_t *reg_oc_dbg) { #if IS_ENABLED(CONFIG_MTK_CCCI_DEVICES) int ret; #endif int data_int32 = 0; if (reg_oc_dbg->is_md_reg) data_int32 = 1 << reg_oc_dbg->md_reg_idx; else return 0; #if IS_ENABLED(CONFIG_MTK_CCCI_DEVICES) ret = exec_ccci_kern_func_by_md_id(MD_SYS1, ID_PMIC_INTR, (char *)&data_int32, 4); if (ret) pr_notice("[%s]-exec_ccci fail:%d\n", __func__, ret); #endif return 0; } static int regulator_oc_notify(struct notifier_block *nb, unsigned long event, void *unused) { char oc_str[30] = ""; struct reg_oc_debug_t *reg_oc_dbg; int ret = 0; reg_oc_dbg = container_of(nb, struct reg_oc_debug_t, nb); if (event != REGULATOR_EVENT_OVER_CURRENT) return NOTIFY_OK; reg_oc_dbg->times++; if (reg_oc_dbg->times > NOTIFY_TIMES_MAX) return NOTIFY_OK; ret = snprintf(oc_str, 30, "PMIC OC:%s", reg_oc_dbg->name); if (ret < 0) pr_info("%s error\n", __func__); pr_notice("regulator:%s OC %d times\n", reg_oc_dbg->name, reg_oc_dbg->times); if (reg_oc_dbg->is_md_reg) { #if IS_ENABLED(CONFIG_MTK_AEE_FEATURE) aee_kernel_warning(oc_str, "\nCRDISPATCH_KEY:MD OC\nOC Interrupt: %s", reg_oc_dbg->name); #endif md_reg_oc_notify(reg_oc_dbg); } else { #if IS_ENABLED(CONFIG_MTK_AEE_FEATURE) aee_kernel_warning(oc_str, "\nCRDISPATCH_KEY:PMIC OC\nOC Interrupt: %s", reg_oc_dbg->name); #endif } return NOTIFY_OK; } static int register_all_oc_notifier(struct platform_device *pdev, struct reg_oc_debug_t *reg_oc_debug, const int array_size) { int i, ret; struct regulator *reg; for (i = 0; i < array_size; i++) { reg = devm_regulator_get_optional(&pdev->dev, reg_oc_debug[i].name); if (PTR_ERR(reg) == -EPROBE_DEFER) return PTR_ERR(reg); else if (IS_ERR(reg)) { dev_notice(&pdev->dev, "fail to get regulator %s\n", reg_oc_debug[i].name); continue; } reg_oc_debug[i].nb.notifier_call = regulator_oc_notify; ret = devm_regulator_register_notifier(reg, ®_oc_debug[i].nb); if (ret) { dev_notice(&pdev->dev, "regulator notifier request failed\n"); } } return 0; } static int mt63xx_oc_debug_probe(struct platform_device *pdev) { struct mt6397_chip *pmic = dev_get_drvdata(pdev->dev.parent); int ret = 0; if (!of_device_is_available(pdev->dev.of_node)) { dev_info(&pdev->dev, "this project no need to enable OC debug\n"); return 0; } regmap = pmic->regmap; if (!regmap) return -ENODEV; switch (pmic->chip_id) { case MT6357_CHIP_ID: ret = register_all_oc_notifier(pdev, mt6357_reg_oc_debug, ARRAY_SIZE(mt6357_reg_oc_debug)); break; case MT6359_CHIP_ID: ret = register_all_oc_notifier(pdev, mt6359_reg_oc_debug, ARRAY_SIZE(mt6359_reg_oc_debug)); break; default: dev_info(&pdev->dev, "unsupported chip: 0x%x\n", pmic->chip_id); ret = -ENODEV; break; } return ret; } static const struct of_device_id mt63xx_oc_debug_of_match[] = { { .compatible = "mediatek,mt63xx-oc-debug", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt63xx_oc_debug_of_match); static struct platform_driver mt63xx_oc_debug_driver = { .driver = { .name = "mt63xx-oc-debug", .of_match_table = mt63xx_oc_debug_of_match, }, .probe = mt63xx_oc_debug_probe, }; module_platform_driver(mt63xx_oc_debug_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jeter Chen "); MODULE_DESCRIPTION("MediaTek PMIC Over Current Debug");