// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include /* For init/exit macros */ #include /* For MODULE_ marcros */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtk_pe.h" #include "mtk_charger_algorithm_class.h" #define VBUS_MAX_DROP 1500000 static int pe_dbg_level = PE_DEBUG_LEVEL; int pe_get_debug_level(void) { return pe_dbg_level; } int mtk_pe_reset_ta_vchr(struct chg_alg_device *alg) { int ret = -1, chr_volt = 0; u32 retry_cnt = 0; struct mtk_pe *pe; bool is_chip_enabled = false; int chg_cnt, i; int mivr; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); pe_hal_set_mivr(alg, CHG1, pe->min_charger_voltage); /* Reset TA's charging voltage */ do { chg_cnt = pe_hal_get_charger_cnt(alg); if (chg_cnt > 1) { for (i = CHG2; i < CHG_MAX; i++) { is_chip_enabled = pe_hal_is_chip_enable(alg, i); if (is_chip_enabled) pe_hal_enable_chip(alg, i, false); } } ret = pe_hal_reset_ta(alg, CHG1); if (ret < 0) pe_err("%s reset TA fail\n", __func__); msleep(250); /* Check charger's voltage */ chr_volt = pe_hal_get_vbus(alg); if (abs(chr_volt - pe->ta_vchr_org) <= 1000000) break; retry_cnt++; } while (retry_cnt < 3); if (retry_cnt >= 3) { pe_err("%s: failed, ret = %d\n", __func__, ret); mivr = chr_volt - 1000000; if (mivr < pe->min_charger_voltage) mivr = pe->min_charger_voltage; pe_hal_set_mivr(alg, CHG1, mivr); /* * SET_INPUT_CURRENT success but chr_volt does not reset to 5V * set ret = -EIO to represent the case */ return -EIO; } /* Enable OVP */ ret = pe_hal_enable_vbus_ovp(alg, true); if (ret < 0) pe_err("%s enable vbus ovp fail\n", __func__); pe_dbg("%s: OK\n", __func__); return 0; } static int pe_leave(struct chg_alg_device *alg, bool disable_charging) { int ret = 0, ret_value; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); /* CV point reached, disable charger */ ret = pe_hal_enable_charging(alg, disable_charging); if (ret < 0) { pe_err("%s enable charging fail:%d\n", __func__, ret); ret_value = -EHAL; } /* Decrease TA voltage to 5V */ ret = mtk_pe_reset_ta_vchr(alg); if (ret < 0) { pe_err("%s reset TA fail:%d\n", __func__, ret); ret_value = -EHAL; } pe_dbg("%s: OK\n", __func__); return ret; } int __pe_increase_ta_vchr(struct chg_alg_device *alg) { int ret = 0, ret_value = 0; bool increase = true; /* Increase */ if (pe_hal_get_charger_type(alg) != POWER_SUPPLY_TYPE_UNKNOWN) { ret = pe_hal_send_ta_current_pattern(alg, increase); if (ret < 0) { pe_err("%s: failed, ret = %d\n", __func__, ret); ret_value = -EHAL; } else pe_dbg("%s: OK\n", __func__); return ret_value; } /* TA is not exist */ ret_value = -ECABLEOUT; pr_notice("%s: failed, cable out\n", __func__); return ret_value; } static int pe_increase_ta_vchr(struct chg_alg_device *alg, u32 vchr_target) { int ret = 0, ret_value = 0; int vchr_before, vchr_after; u32 retry_cnt = 0; bool is_chip_enabled = false; int chg_cnt, i; do { chg_cnt = pe_hal_get_charger_cnt(alg); if (chg_cnt > 1) { for (i = CHG2; i < CHG_MAX; i++) { is_chip_enabled = pe_hal_is_chip_enable(alg, i); if (is_chip_enabled) ret = pe_hal_enable_chip(alg, i, false); if (ret < 0) pe_err("enable chip fail %d\n", i); } } vchr_before = pe_hal_get_vbus(alg); __pe_increase_ta_vchr(alg); vchr_after = pe_hal_get_vbus(alg); if (abs(vchr_after - vchr_target) <= VBUS_MAX_DROP) { pe_dbg("%s: OK\n", __func__); return ret_value; } pe_dbg("%s: retry, cnt = %d, vchr = (%d, %d), vchr_target = %d\n", __func__, retry_cnt, vchr_before / 1000, vchr_after / 1000, vchr_target / 1000); retry_cnt++; } while (pe_hal_get_charger_type(alg) != POWER_SUPPLY_TYPE_UNKNOWN && retry_cnt < 3); ret = -EHAL; pe_dbg("%s: failed, vchr = (%d, %d), vchr_target = %d\n", __func__, vchr_before / 1000, vchr_after / 1000, vchr_target / 1000); return ret; } static int pe_detect_ta(struct chg_alg_device *alg) { int ret = 0; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); /* Disable OVP */ ret = pe_hal_enable_vbus_ovp(alg, false); if (ret < 0) goto _err; pe->ta_vchr_org = pe_hal_get_vbus(alg); ret = pe_increase_ta_vchr(alg, 7000000); /* uv */ if (ret == 0) { pe_dbg("%s: OK\n", __func__); return ret; } /* Detect PE+ TA failed */ ret = -1; /* Enable OVP */ pe_hal_enable_vbus_ovp(alg, true); /* Set MIVR for vbus 5V */ pe_hal_set_mivr(alg, CHG1, pe->min_charger_voltage); _err: pe_err("%s: failed, ret:%d\n", __func__, ret); return ret; } static int pe_init_ta(struct chg_alg_device *alg) { int ret = 0; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); return ret; } static int pe_plugout_reset(struct chg_alg_device *alg) { int ret = 0; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); /* pe is not running */ if (pe->state != PE_RUN && pe->state != PE_DONE) { pe->state = PE_HW_READY; pe_err("%s:not running,state:%d\n", __func__, pe->state); return ret; } if (pe->state != PE_HW_FAIL) pe->state = PE_HW_READY; ret = mtk_pe_reset_ta_vchr(alg); if (ret < 0) goto _err; pe_dbg("%s: OK\n", __func__); return ret; _err: pe_err("%s: failed, ret = %d\n", __func__, ret); return ret; } int __pe_check_charger(struct chg_alg_device *alg) { int ret = 0, uisoc, ret_value = 0; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); uisoc = pe_hal_get_uisoc(alg); pe_dbg("%s uisoc:%d s:%d end:%d type:%d", __func__, uisoc, pe->ta_start_battery_soc, pe->ta_stop_battery_soc, pe_hal_get_charger_type(alg)); if (pe_hal_get_charger_type(alg) != POWER_SUPPLY_TYPE_USB_DCP) { ret_value = ALG_TA_NOT_SUPPORT; goto out; } if (uisoc < pe->ta_start_battery_soc || uisoc >= pe->ta_stop_battery_soc) { ret_value = ALG_TA_CHECKING; goto out; } if (pe->is_cable_out_occur) goto out; /* Reset/Init/Detect TA */ ret = mtk_pe_reset_ta_vchr(alg); if (ret < 0) goto out; if (pe->is_cable_out_occur) goto out; ret = pe_init_ta(alg); if (ret < 0) goto out; if (pe->is_cable_out_occur) goto out; ret = pe_detect_ta(alg); if (ret < 0) goto out; pe_dbg("%s: OK\n", __func__); return ret_value; out: if (ret_value == 0) ret_value = ALG_TA_NOT_SUPPORT; pe_dbg("%s: stop, SOC:%d, chr_type:%d, ret:%d:%d\n", __func__, pe_hal_get_uisoc(alg), pe_hal_get_charger_type(alg), ret, ret_value); return ret_value; } int mtk_pe_set_charging_current(struct chg_alg_device *alg) { int ret = 0, chr_volt = 0; struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); chr_volt = pe_hal_get_vbus(alg); if ((chr_volt - pe->ta_vchr_org) > 6000000) { /* TA = 12V */ pe->input_current = pe->ta_ac_12v_input_current; pe->charging_current = pe->ta_ac_charger_current; } else if ((chr_volt - pe->ta_vchr_org) > 3000000) { /* TA = 9V */ pe->input_current = pe->ta_ac_9v_input_current; pe->charging_current = pe->ta_ac_charger_current; } else if ((chr_volt - pe->ta_vchr_org) > 1000000) { /* TA = 7V */ pe->input_current = pe->ta_ac_7v_input_current; pe->charging_current = pe->ta_ac_charger_current; } if (pe->charging_current_limit != -1 && pe->charging_current_limit < pe->charging_current) pe->charging_current = pe->charging_current_limit; if (pe->input_current_limit != -1 && pe->input_current_limit < pe->input_current) pe->input_current = pe->input_current_limit; pe_hal_set_charging_current(alg, CHG1, pe->input_current); pe_hal_set_input_current(alg, CHG1, pe->charging_current); pe_hal_set_cv(alg, CHG1, pe->cv); pe_dbg("%s cv:%d icl:%d cc:%d chr_org:%d chr_after:%d\n", __func__, pe->cv, pe->input_current, pe->charging_current, pe->ta_vchr_org / 1000, chr_volt / 1000); return ret; } static char *pe_state_to_str(int state) { switch (state) { case PE_HW_UNINIT: return "PE_HW_UNINIT"; break; case PE_HW_FAIL: return "PE_HW_FAIL"; break; case PE_HW_READY: return "PE_HW_READY"; break; case PE_TA_NOT_SUPPORT: return "PE_TA_NOT_SUPPORT"; break; case PE_RUN: return "PE_RUN"; break; case PE_DONE: return "PE_DONE"; break; default: break; } pe_err("%s unknown state:%d\n", __func__ , state); return "PE_UNKNOWN"; } static int _pe_is_algo_ready(struct chg_alg_device *alg) { struct mtk_pe *pe; int ret_value = 0, uisoc; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s state:%s\n", __func__, pe_state_to_str(pe->state)); switch (pe->state) { case PE_HW_UNINIT: case PE_HW_FAIL: ret_value = ALG_INIT_FAIL; break; case PE_HW_READY: uisoc = pe_hal_get_uisoc(alg); if (pe_hal_get_charger_type(alg) != POWER_SUPPLY_TYPE_USB_DCP) { ret_value = ALG_TA_NOT_SUPPORT; } else if (uisoc < pe->ta_start_battery_soc || uisoc >= pe->ta_stop_battery_soc) { ret_value = ALG_NOT_READY; } else { ret_value = ALG_READY; } break; case PE_TA_NOT_SUPPORT: ret_value = ALG_TA_NOT_SUPPORT; break; case PE_RUN: ret_value = ALG_RUNNING; break; case PE_DONE: ret_value = ALG_DONE; break; default: break; } return ret_value; } static int _pe_init_algo(struct chg_alg_device *alg) { struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s\n", __func__); if (pe_hal_init_hardware(alg) != 0) pe->state = PE_HW_FAIL; else pe->state = PE_HW_READY; return 0; } static bool _pe_is_algo_running(struct chg_alg_device *alg) { struct mtk_pe *pe; pe_dbg("%s\n", __func__); pe = dev_get_drvdata(&alg->dev); if (pe->state == PE_RUN) return true; return false; } static int _pe_stop_algo(struct chg_alg_device *alg) { struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s %d\n", __func__, pe->state); if (pe->state == PE_RUN) { mtk_pe_reset_ta_vchr(alg); pe->state = PE_HW_READY; } return 0; } static int _pe_notifier_call(struct chg_alg_device *alg, struct chg_alg_notify *notify) { struct mtk_pe *pe; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s evt:%d\n", __func__, notify->evt); pe_dbg("%s state:%d %s\n", __func__, pe->state, pe_state_to_str(pe->state)); switch (notify->evt) { case EVT_PLUG_OUT: pe_plugout_reset(alg); break; case EVT_FULL: if (pe->state == PE_RUN) { pe_err("%s evt full\n", __func__); pe_leave(alg, true); pe->state = PE_DONE; } break; case EVT_RECHARGE: if (pe->state == PE_DONE) { pe_err("%s evt recharge\n", __func__); pe->state = PE_HW_READY; } break; default: return -EINVAL; } return 0; } static int pe_get_conditional_vbus(struct chg_alg_device *alg, u32 uA) { int chr_volt = 0, orig_chr_current = 0; pe_hal_get_charging_current(alg, CHG1, &orig_chr_current); pe_hal_set_charging_current(alg, CHG1, uA); msleep(20); chr_volt = pe_hal_get_vbus(alg); pe_hal_set_charging_current(alg, CHG1, orig_chr_current); return chr_volt; } int _pe_get_status(struct chg_alg_device *alg, enum chg_alg_props s, int *value) { pe_dbg("%s\n", __func__); if (s == ALG_MAX_VBUS) *value = 12000; else pe_dbg("%s does not support prop:%d\n", __func__, s); return 0; } int _pe_set_setting(struct chg_alg_device *alg_dev, struct chg_limit_setting *setting) { struct mtk_pe *pe; pe_dbg("%s cv:%d icl:%d cc:%d\n", __func__, setting->cv, setting->input_current_limit1, setting->charging_current_limit1); pe = dev_get_drvdata(&alg_dev->dev); pe->cv = setting->cv; pe->input_current_limit = setting->input_current_limit1; pe->charging_current_limit = setting->charging_current_limit1; return 0; } static int __pe_run(struct chg_alg_device *alg) { struct mtk_pe *pe; int ret = 0, chr_volt, chr_volt2, ret_value = 0, ichg; bool tune = false; int mivr; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s: starts\n", __func__); if (pe->is_cable_out_occur) goto _out; chr_volt = pe_hal_get_vbus(alg); chr_volt2 = pe_get_conditional_vbus(alg, 500000); if (pe->ta_9v_support && pe->ta_12v_support) { if (abs(chr_volt - 12000000) > VBUS_MAX_DROP) { if (abs(chr_volt2 - 12000000) > VBUS_MAX_DROP) { tune = true; } else { pe_err("%s: V drop out of range, skip pe", __func__); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } } pe_dbg("%s: vbus:%d target:%d 9v:%d 12v:%d tune:%d\n", __func__, chr_volt, 12000000, pe->ta_9v_support, pe->ta_12v_support, tune); } else if (pe->ta_9v_support && !pe->ta_12v_support) { if (abs(chr_volt - 9000000) > VBUS_MAX_DROP) { if (abs(chr_volt2 - 9000000) > VBUS_MAX_DROP) { tune = true; } else { pe_err("%s: V drop out of range, skip pe", __func__); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } } pe_dbg("%s: vbus:%d target:%d 9v:%d 12v:%d tune:%d\n", __func__, chr_volt, 9000000, pe->ta_9v_support, pe->ta_12v_support, tune); } else { tune = false; pe_dbg("%s:error setting vbus:%d 9v:%d 12v:%d tune:%d\n", __func__, chr_volt, pe->ta_9v_support, pe->ta_12v_support, tune); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } if (tune == true) { /* Increase TA voltage to 9V */ if (pe->ta_9v_support || pe->ta_12v_support) { ret = pe_increase_ta_vchr(alg, 9000000); /* uv */ if (ret < 0) { pe_err("%s: failed, cannot increase to 9V\n", __func__); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } /* Successfully, increase to 9V */ pe_dbg("%s: output 9V ok,vbus:%d\n", __func__, pe_hal_get_vbus(alg)); } /* Increase TA voltage to 12V */ if (pe->ta_12v_support) { ret = pe_increase_ta_vchr(alg, 12000000); /* uv */ if (ret < 0) { pe_err("%s: failed, cannot increase to 12V\n", __func__); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } /* Successfully, increase to 12V */ pe_dbg("%s: output 12V ok,vbus:%d\n", __func__, pe_hal_get_vbus(alg)); } } else { ichg = pe_hal_get_ibat(alg); /* Check SOC & Ichg */ if (pe->ta_stop_battery_soc < pe_hal_get_uisoc(alg) && ichg > 0 && ichg < pe->pe_ichg_level_threshold) { ret_value = ALG_DONE; pe_dbg("%s: OK, SOC = (%d,%d), Ichg:%dmA,ret:%d stop PE+\n", __func__, pe_hal_get_uisoc(alg), pe->ta_stop_battery_soc, ichg / 1000, ret); pe_leave(alg, true); goto _out; } } mtk_pe_set_charging_current(alg); chr_volt = pe_hal_get_vbus(alg); mivr = chr_volt - 1000000; if (mivr < pe->min_charger_voltage) mivr = pe->min_charger_voltage; ret = pe_hal_set_mivr(alg, CHG1, mivr); if (ret < 0) { pe_err("%s: set mivr fail\n", __func__); ret_value = ALG_TA_NOT_SUPPORT; goto _err; } pe_dbg("%s: vchr_org = %d, vchr_after = %d, delta = %d\n", __func__, pe->ta_vchr_org / 1000, chr_volt / 1000, (chr_volt - pe->ta_vchr_org) / 1000); pe_dbg("%s: OK\n", __func__); return ret_value; _err: pe_leave(alg, false); _out: if (ret_value != 0) ret_value = ALG_TA_NOT_SUPPORT; chr_volt = pe_hal_get_vbus(alg); pr_debug("%s: vchr_org = %d, vchr_after = %d, delta = %d\n", __func__, pe->ta_vchr_org / 1000, chr_volt / 1000, (chr_volt - pe->ta_vchr_org) / 1000); return ret_value; } int _pe_start_algo(struct chg_alg_device *alg) { struct mtk_pe *pe; bool again; int ret, ret_value; pe = dev_get_drvdata(&alg->dev); pe_dbg("%s state:%d %s\n", __func__, pe->state, pe_state_to_str(pe->state)); /* Lock */ mutex_lock(&pe->access_lock); __pm_stay_awake(pe->suspend_lock); do { pe_info("%s state:%d %s %d\n", __func__, pe->state, pe_state_to_str(pe->state), again); again = false; switch (pe->state) { case PE_HW_UNINIT: case PE_HW_FAIL: ret_value = ALG_INIT_FAIL; break; case PE_HW_READY: ret = __pe_check_charger(alg); if (ret == 0) { pe->state = PE_RUN; ret_value = ALG_READY; again = true; } else if (ret == ALG_TA_CHECKING) ret_value = ALG_TA_CHECKING; else { pe->state = PE_TA_NOT_SUPPORT; ret_value = ALG_TA_NOT_SUPPORT; } break; case PE_TA_NOT_SUPPORT: ret_value = ALG_TA_NOT_SUPPORT; break; case PE_RUN: ret = __pe_run(alg); if (ret == ALG_TA_NOT_SUPPORT) pe->state = PE_TA_NOT_SUPPORT; else if (ret == ALG_TA_CHECKING) { pe->state = PE_RUN; again = true; } else if (ret == ALG_DONE) pe->state = PE_DONE; ret_value = ret; break; case PE_DONE: ret_value = ALG_DONE; break; default: pe_err("PE unknown state:%d\n", pe->state); ret_value = ALG_INIT_FAIL; break; } } while (again == true); __pm_relax(pe->suspend_lock); mutex_unlock(&pe->access_lock); return ret; } static struct chg_alg_ops pe_alg_ops = { .init_algo = _pe_init_algo, .is_algo_ready = _pe_is_algo_ready, .start_algo = _pe_start_algo, .is_algo_running = _pe_is_algo_running, .stop_algo = _pe_stop_algo, .notifier_call = _pe_notifier_call, .get_prop = _pe_get_status, .set_current_limit = _pe_set_setting, }; static void mtk_pe_parse_dt(struct mtk_pe *pe, struct device *dev) { struct device_node *np = dev->of_node; u32 val; /* PE */ pe->ta_12v_support = of_property_read_bool(np, "ta_12v_support"); pe->ta_9v_support = of_property_read_bool(np, "ta_9v_support"); if (of_property_read_u32(np, "pe_ichg_level_threshold", &val) >= 0) pe->pe_ichg_level_threshold = val; else { pr_notice("use default PE_ICHG_LEAVE_THRESHOLD:%d\n", PE_ICHG_LEAVE_THRESHOLD); pe->pe_ichg_level_threshold = PE_ICHG_LEAVE_THRESHOLD; } if (of_property_read_u32(np, "ta_start_battery_soc", &val) >= 0) pe->ta_start_battery_soc = val; else { pr_notice("use default TA_START_BATTERY_SOC:%d\n", TA_START_BATTERY_SOC); pe->ta_start_battery_soc = TA_START_BATTERY_SOC; } if (of_property_read_u32(np, "ta_stop_battery_soc", &val) >= 0) pe->ta_stop_battery_soc = val; else { pr_notice("use default TA_STOP_BATTERY_SOC:%d\n", TA_STOP_BATTERY_SOC); pe->ta_stop_battery_soc = TA_STOP_BATTERY_SOC; } if (of_property_read_u32(np, "min_charger_voltage", &val) >= 0) pe->min_charger_voltage = val; else { pr_notice("use default V_CHARGER_MIN:%d\n", PE_V_CHARGER_MIN); pe->min_charger_voltage = PE_V_CHARGER_MIN; } if (of_property_read_u32(np, "ta_ac_12v_input_current", &val) >= 0) pe->ta_ac_12v_input_current = val; else { pr_notice("use default TA_AC_12V_INPUT_CURRENT:%d\n", TA_AC_12V_INPUT_CURRENT); pe->ta_ac_12v_input_current = TA_AC_12V_INPUT_CURRENT; } if (of_property_read_u32(np, "ta_ac_9v_input_current", &val) >= 0) pe->ta_ac_9v_input_current = val; else { pr_notice("use default TA_AC_9V_INPUT_CURRENT:%d\n", TA_AC_9V_INPUT_CURRENT); pe->ta_ac_9v_input_current = TA_AC_9V_INPUT_CURRENT; } if (of_property_read_u32(np, "ta_ac_7v_input_current", &val) >= 0) pe->ta_ac_7v_input_current = val; else { pr_notice("use default TA_AC_7V_INPUT_CURRENT:%d\n", TA_AC_7V_INPUT_CURRENT); pe->ta_ac_7v_input_current = TA_AC_7V_INPUT_CURRENT; } if (of_property_read_u32(np, "pe_charger_current", &val) >= 0) pe->ta_ac_charger_current = val; else { pr_notice("use default pe_charger_current:%d\n", PE_CHARGING_CURRENT); pe->ta_ac_charger_current = PE_CHARGING_CURRENT; } } static int mtk_pe_probe(struct platform_device *pdev) { struct mtk_pe *pe = NULL; pr_notice("%s: starts\n", __func__); pe = devm_kzalloc(&pdev->dev, sizeof(*pe), GFP_KERNEL); if (!pe) return -ENOMEM; platform_set_drvdata(pdev, pe); pe->pdev = pdev; pe->suspend_lock = wakeup_source_register(NULL, "PE+ suspend wakelock"); mutex_init(&pe->access_lock); mutex_init(&pe->cable_out_lock); pe->ta_vchr_org = 5000000; mtk_pe_parse_dt(pe, &pdev->dev); pe->bat_psy = devm_power_supply_get_by_phandle(&pdev->dev, "gauge"); if (IS_ERR_OR_NULL(pe->bat_psy)) pe_err("%s: devm power fail to get bat_psy\n", __func__); pe->alg = chg_alg_device_register("pe", &pdev->dev, pe, &pe_alg_ops, NULL); return 0; } static int mtk_pe_remove(struct platform_device *dev) { return 0; } static void mtk_pe_shutdown(struct platform_device *dev) { } static const struct of_device_id mtk_pe_of_match[] = { {.compatible = "mediatek,charger,pe",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_pe_of_match); struct platform_device pe_device = { .name = "pe", .id = -1, }; static struct platform_driver pe_driver = { .probe = mtk_pe_probe, .remove = mtk_pe_remove, .shutdown = mtk_pe_shutdown, .driver = { .name = "pe", .of_match_table = mtk_pe_of_match, }, }; static int __init mtk_pe_init(void) { return platform_driver_register(&pe_driver); } late_initcall(mtk_pe_init); static void __exit mtk_pe_exit(void) { platform_driver_unregister(&pe_driver); } module_exit(mtk_pe_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK Pump Express algorithm Driver"); MODULE_LICENSE("GPL");