// SPDX-License-Identifier: GPL-2.0 // // Copyright (c) 2021 MediaTek Inc. #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_MTK_AEE_FEATURE) #include #endif #include #define NOTIFY_TIMES_MAX 2 struct oc_debug_t { const char *name; struct notifier_block nb; unsigned int times; bool is_md_reg; char md_data; }; struct oc_debug_info { struct oc_debug_t *oc_debug; size_t oc_debug_num; }; #define REG_OC_DEBUG(_name) \ { \ .name = #_name, \ } #define MD_REG_OC_DEBUG(_name, _md_data)\ { \ .name = #_name, \ .is_md_reg = true, \ .md_data = _md_data, \ } static struct oc_debug_t mt6835_oc_debug[] = { MD_REG_OC_DEBUG(VPA, BIT(0)), REG_OC_DEBUG(mt6377_vaux18), REG_OC_DEBUG(mt6377_vbif28), REG_OC_DEBUG(mt6377_vcn33_1), REG_OC_DEBUG(mt6377_vcn33_2), REG_OC_DEBUG(mt6377_vcn18), REG_OC_DEBUG(mt6377_vm18), REG_OC_DEBUG(mt6377_vefuse), REG_OC_DEBUG(mt6377_vemc), REG_OC_DEBUG(mt6377_vufs), REG_OC_DEBUG(mt6377_vio18), MD_REG_OC_DEBUG(mt6377_vrf18, BIT(4)), REG_OC_DEBUG(mt6377_vrf12), MD_REG_OC_DEBUG(mt6377_vrf09, BIT(2)), REG_OC_DEBUG(mt6377_vrfva12), MD_REG_OC_DEBUG(mt6377_vrfio18, BIT(5)), REG_OC_DEBUG(mt6377_vusb), REG_OC_DEBUG(mt6377_vio28), REG_OC_DEBUG(mt6377_vfp), }; static struct oc_debug_t mt6879_oc_debug[] = { MD_REG_OC_DEBUG(mt6363_vcn15, BIT(5)), REG_OC_DEBUG(mt6363_vcn13), MD_REG_OC_DEBUG(mt6363_vrf09, BIT(2)), REG_OC_DEBUG(mt6363_vrf12), MD_REG_OC_DEBUG(mt6363_vrf13, BIT(3)), MD_REG_OC_DEBUG(mt6363_vrf18, BIT(4)), REG_OC_DEBUG(mt6363_vrfio18), REG_OC_DEBUG(mt6363_vsram_mdfe), REG_OC_DEBUG(mt6363_vtref18), REG_OC_DEBUG(mt6363_vsram_apu), REG_OC_DEBUG(mt6363_vaux18), REG_OC_DEBUG(mt6363_vemc), REG_OC_DEBUG(mt6363_vufs12), REG_OC_DEBUG(mt6363_vufs18), REG_OC_DEBUG(mt6363_vio18), REG_OC_DEBUG(mt6363_vio075), REG_OC_DEBUG(mt6363_va12_1), REG_OC_DEBUG(mt6363_va12_2), REG_OC_DEBUG(mt6363_va15), REG_OC_DEBUG(mt6363_vm18), REG_OC_DEBUG(mt6368_vusb), REG_OC_DEBUG(mt6368_vaux18), REG_OC_DEBUG(mt6368_vrf13_aif), REG_OC_DEBUG(mt6368_vrf18_aif), REG_OC_DEBUG(mt6368_vant18), REG_OC_DEBUG(mt6368_vio28), REG_OC_DEBUG(mt6368_vfp), REG_OC_DEBUG(mt6368_vcn33_1), REG_OC_DEBUG(mt6368_vcn33_2), REG_OC_DEBUG(mt6368_vefuse), MD_REG_OC_DEBUG(VPA, BIT(0)), }; static struct oc_debug_t mt6983_oc_debug[] = { MD_REG_OC_DEBUG(mt6363_vcn15, BIT(5)), REG_OC_DEBUG(mt6363_vcn13), MD_REG_OC_DEBUG(mt6363_vrf09, BIT(2)), REG_OC_DEBUG(mt6363_vrf12), MD_REG_OC_DEBUG(mt6363_vrf13, BIT(3)), MD_REG_OC_DEBUG(mt6363_vrf18, BIT(4)), REG_OC_DEBUG(mt6363_vrfio18), REG_OC_DEBUG(mt6363_vsram_mdfe), REG_OC_DEBUG(mt6363_vtref18), REG_OC_DEBUG(mt6363_vsram_apu), REG_OC_DEBUG(mt6363_vaux18), REG_OC_DEBUG(mt6363_vemc), REG_OC_DEBUG(mt6363_vufs12), REG_OC_DEBUG(mt6363_vufs18), REG_OC_DEBUG(mt6363_vio18), REG_OC_DEBUG(mt6363_vio075), REG_OC_DEBUG(mt6363_va12_1), REG_OC_DEBUG(mt6363_va12_2), REG_OC_DEBUG(mt6363_va15), REG_OC_DEBUG(mt6363_vm18), REG_OC_DEBUG(mt6373_vusb), REG_OC_DEBUG(mt6373_vaux18), REG_OC_DEBUG(mt6373_vrf13_aif), REG_OC_DEBUG(mt6373_vrf18_aif), REG_OC_DEBUG(mt6373_vrfio18_aif), REG_OC_DEBUG(mt6373_vcn33_1), REG_OC_DEBUG(mt6373_vcn33_2), REG_OC_DEBUG(mt6373_vcn33_3), REG_OC_DEBUG(mt6373_vcn18io), REG_OC_DEBUG(mt6373_vrf09_aif), REG_OC_DEBUG(mt6373_vrf12_aif), REG_OC_DEBUG(mt6373_vant18), REG_OC_DEBUG(mt6373_vsram_digrf_aif), REG_OC_DEBUG(mt6373_vefuse), REG_OC_DEBUG(mt6373_vio28), REG_OC_DEBUG(mt6373_vfp), }; static struct oc_debug_info mt6835_debug_info = { .oc_debug = mt6835_oc_debug, .oc_debug_num = ARRAY_SIZE(mt6835_oc_debug), }; static struct oc_debug_info mt6879_debug_info = { .oc_debug = mt6879_oc_debug, .oc_debug_num = ARRAY_SIZE(mt6879_oc_debug), }; static struct oc_debug_info mt6983_debug_info = { .oc_debug = mt6983_oc_debug, .oc_debug_num = ARRAY_SIZE(mt6983_oc_debug), }; static int md_oc_notify(struct oc_debug_t *oc_dbg) { int ret; ret = exec_ccci_kern_func(ID_PMIC_INTR, &oc_dbg->md_data, 4); if (ret) pr_notice("[%s]-exec_ccci fail:%d\n", __func__, ret); return 0; } static int regulator_oc_notify(struct notifier_block *nb, unsigned long event, void *unused) { unsigned int len = 0; char oc_str[30] = ""; struct oc_debug_t *oc_dbg; if (event != REGULATOR_EVENT_OVER_CURRENT) return NOTIFY_OK; oc_dbg = container_of(nb, struct oc_debug_t, nb); oc_dbg->times++; if (oc_dbg->times > NOTIFY_TIMES_MAX) { if (oc_dbg->is_md_reg) md_oc_notify(oc_dbg); return NOTIFY_OK; } pr_notice("regulator:%s OC %d times\n", oc_dbg->name, oc_dbg->times); len += snprintf(oc_str, 30, "PMIC OC:%s", oc_dbg->name); if (oc_dbg->is_md_reg) { #if IS_ENABLED(CONFIG_MTK_AEE_FEATURE) aee_kernel_warning(oc_str, "\nCRDISPATCH_KEY:MD OC\nOC Interrupt: %s", oc_dbg->name); #endif md_oc_notify(oc_dbg); } else if (oc_dbg->times == NOTIFY_TIMES_MAX) { #if IS_ENABLED(CONFIG_MTK_AEE_FEATURE) aee_kernel_warning(oc_str, "\nCRDISPATCH_KEY:PMIC OC\nOC Interrupt: %s", oc_dbg->name); #endif } return NOTIFY_OK; } static int register_oc_notifier(struct platform_device *pdev, struct oc_debug_t *oc_debug, size_t oc_debug_num) { int i, ret; struct regulator *reg = NULL; for (i = 0; i < oc_debug_num; i++, oc_debug++) { reg = devm_regulator_get_optional(&pdev->dev, oc_debug->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", oc_debug->name); continue; } oc_debug->nb.notifier_call = regulator_oc_notify; ret = devm_regulator_register_notifier(reg, &oc_debug->nb); if (ret) { dev_notice(&pdev->dev, "regulator notifier request failed\n"); } } return 0; } struct vio18_ctrl_t { struct regmap *main_regmap; struct regmap *second_regmap; unsigned int main_switch; unsigned int second_switch; }; static irqreturn_t lvsys_f_irq_handler(int irq, void *data) { int ret; struct vio18_ctrl_t *vio18_ctrl = (struct vio18_ctrl_t *)data; ret = regmap_write(vio18_ctrl->main_regmap, vio18_ctrl->main_switch, 0); if (ret) pr_notice("Failed to set main vio18_switch, ret=%d\n", ret); ret = regmap_write(vio18_ctrl->second_regmap, vio18_ctrl->second_switch, 0); if (ret) pr_notice("Failed to set second vio18_switch, ret=%d\n", ret); return IRQ_HANDLED; } static irqreturn_t lvsys_r_irq_handler(int irq, void *data) { int ret; struct vio18_ctrl_t *vio18_ctrl = (struct vio18_ctrl_t *)data; ret = regmap_write(vio18_ctrl->main_regmap, vio18_ctrl->main_switch, 1); if (ret) pr_notice("Failed to set main vio18_switch, ret=%d\n", ret); ret = regmap_write(vio18_ctrl->second_regmap, vio18_ctrl->second_switch, 1); if (ret) pr_notice("Failed to set second vio18_switch, ret=%d\n", ret); return IRQ_HANDLED; } static struct regmap *vio18_switch_get_regmap(const char *name) { struct device_node *np; struct platform_device *pdev; np = of_find_node_by_name(NULL, name); if (!np) return NULL; pdev = of_find_device_by_node(np->child); if (!pdev) return NULL; return dev_get_regmap(pdev->dev.parent, NULL); } static int vio18_switch(struct platform_device *pdev, struct oc_debug_info *info) { int ret; int irq_f, irq_r; struct vio18_ctrl_t *vio18_ctrl; vio18_ctrl = devm_kzalloc(&pdev->dev, sizeof(*vio18_ctrl), GFP_KERNEL); if (!vio18_ctrl) return -ENOMEM; vio18_ctrl->main_switch = 0x53; vio18_ctrl->main_regmap = vio18_switch_get_regmap("pmic"); if (!vio18_ctrl->main_regmap) return -EINVAL; if (info == &mt6879_debug_info) vio18_ctrl->second_switch = 0x57; else if (info == &mt6983_debug_info) vio18_ctrl->second_switch = 0x58; else return -EINVAL; vio18_ctrl->second_regmap = vio18_switch_get_regmap("second_pmic"); if (!vio18_ctrl->second_regmap) return -EINVAL; irq_f = platform_get_irq_byname_optional(pdev, "LVSYS_F"); if (irq_f < 0) return irq_f; irq_r = platform_get_irq_byname_optional(pdev, "LVSYS_R"); if (irq_r < 0) return irq_r; ret = devm_request_threaded_irq(&pdev->dev, irq_f, NULL, lvsys_f_irq_handler, IRQF_ONESHOT, "vio18_switch", vio18_ctrl); if (ret < 0) return ret; ret = devm_request_threaded_irq(&pdev->dev, irq_r, NULL, lvsys_r_irq_handler, IRQF_ONESHOT, "vio18_switch", vio18_ctrl); if (ret < 0) return ret; /* RG_LVSYS_INT_VTHL = 0x9 (3.4V) */ ret = regmap_write(vio18_ctrl->main_regmap, 0xA8B, 0x9); if (ret) { dev_notice(&pdev->dev, "Failed to set LVSYS_INT_VTHL, ret=%d\n", ret); return ret; } /* RG_LVSYS_INT_VTHH = 0x9 (3.5V) */ ret = regmap_write(vio18_ctrl->main_regmap, 0xA8C, 0x9); if (ret) { dev_notice(&pdev->dev, "Failed to set LVSYS_INT_VTHH, ret=%d\n", ret); return ret; } /* RG_LVSYS_INT_EN = 0x1 */ ret = regmap_update_bits(vio18_ctrl->main_regmap, 0xA18, 1, 1); if (ret) dev_notice(&pdev->dev, "Failed to enable LVSYS_INT, ret=%d\n", ret); return ret; } static int pmic_oc_debug_probe(struct platform_device *pdev) { struct oc_debug_info *info; info = (struct oc_debug_info *)of_device_get_match_data(&pdev->dev); if (!info) { dev_info(&pdev->dev, "this chip no need to enable OC debug\n"); return 0; } if (vio18_switch(pdev, info) != 0) dev_info(&pdev->dev, "vio18_switch init failed\n"); return register_oc_notifier(pdev, info->oc_debug, info->oc_debug_num); } static const struct of_device_id pmic_oc_debug_of_match[] = { { .compatible = "mediatek,mt6835-oc-debug", .data = &mt6835_debug_info, }, { .compatible = "mediatek,mt6879-oc-debug", .data = &mt6879_debug_info, }, { .compatible = "mediatek,mt6895-oc-debug", .data = &mt6879_debug_info, }, { .compatible = "mediatek,mt6983-oc-debug", .data = &mt6983_debug_info, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pmic_oc_debug_of_match); static struct platform_driver pmic_oc_debug_driver = { .driver = { .name = "pmic-oc-debug", .of_match_table = pmic_oc_debug_of_match, }, .probe = pmic_oc_debug_probe, }; module_platform_driver(pmic_oc_debug_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jeter Chen "); MODULE_DESCRIPTION("MediaTek PMIC Over Current Debug");