/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2021 MediaTek Inc. */ /* * * Filename: * --------- * mtk_charger.c * * Project: * -------- * Android_Software * * Description: * ------------ * This Module defines functions of Battery charging * * Author: * ------- * Wy Chuang * */ #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 #include #include #include #include #include "mtk_charger_intf.h" #include "mtk_charger_init.h" #include #if defined(CONFIG_BATTERY_SAMSUNG) #if defined(CONFIG_PDIC_NOTIFIER) #include #endif #include <../drivers/battery/common/sec_charging_common.h> extern struct pdic_notifier_struct pd_noti; extern int pdc_clear(void); extern int pdc_hard_rst(void); extern int f_mode_battery; #endif static struct charger_manager *pinfo; static struct list_head consumer_head = LIST_HEAD_INIT(consumer_head); static DEFINE_MUTEX(consumer_mutex); struct tag_bootmode { u32 size; u32 tag; u32 bootmode; u32 boottype; }; bool mtk_is_TA_support_pd_pps(struct charger_manager *pinfo) { if (pinfo->enable_pe_4 == false && pinfo->enable_pe_5 == false) return false; if (pinfo->pd_type == MTK_PD_CONNECT_PE_READY_SNK_APDO) return true; return false; } bool is_power_path_supported(void) { if (pinfo == NULL) return false; if (pinfo->data.power_path_support == true) return true; return false; } bool is_disable_charger(void) { if (pinfo == NULL) return true; if (pinfo->disable_charger == true || IS_ENABLED(CONFIG_POWER_EXT)) return true; else return false; } void BATTERY_SetUSBState(int usb_state_value) { if (is_disable_charger()) { chr_err("[%s] in FPGA/EVB, no service\n", __func__); } else { if ((usb_state_value < USB_SUSPEND) || ((usb_state_value > USB_CONFIGURED))) { chr_err("%s Fail! Restore to default value\n", __func__); usb_state_value = USB_UNCONFIGURED; } else { chr_err("%s Success! Set %d\n", __func__, usb_state_value); if (pinfo) pinfo->usb_state = usb_state_value; } } } unsigned int set_chr_input_current_limit(int current_limit) { return 500; } int get_chr_temperature(int *min_temp, int *max_temp) { *min_temp = 25; *max_temp = 30; return 0; } int set_chr_boost_current_limit(unsigned int current_limit) { return 0; } int set_chr_enable_otg(unsigned int enable) { return 0; } int mtk_chr_is_charger_exist(unsigned char *exist) { if (mt_get_charger_type() == CHARGER_UNKNOWN) *exist = 0; else *exist = 1; return 0; } /*=============== fix me==================*/ int chargerlog_level = CHRLOG_ERROR_LEVEL; int chr_get_debug_level(void) { return chargerlog_level; } #ifdef MTK_CHARGER_EXP #include char chargerlog[1000]; #define LOG_LENGTH 500 int chargerlog_level = 10; int chargerlogIdx; int charger_get_debug_level(void) { return chargerlog_level; } void charger_log(const char *fmt, ...) { va_list args; va_start(args, fmt); vsprintf(chargerlog + chargerlogIdx, fmt, args); va_end(args); chargerlogIdx = strlen(chargerlog); if (chargerlogIdx >= LOG_LENGTH) { chr_err("%s", chargerlog); chargerlogIdx = 0; memset(chargerlog, 0, 1000); } } void charger_log_flash(const char *fmt, ...) { va_list args; va_start(args, fmt); vsprintf(chargerlog + chargerlogIdx, fmt, args); va_end(args); chr_err("%s", chargerlog); chargerlogIdx = 0; memset(chargerlog, 0, 1000); } #endif void _wake_up_charger(struct charger_manager *info) { unsigned long flags; if (info == NULL) return; spin_lock_irqsave(&info->slock, flags); if (!info->charger_wakelock->active) __pm_stay_awake(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); info->charger_thread_timeout = true; wake_up_interruptible(&info->wait_que); } /* charger_manager ops */ static int _mtk_charger_change_current_setting(struct charger_manager *info) { if (info != NULL && info->change_current_setting) return info->change_current_setting(info); return 0; } static int _mtk_charger_do_charging(struct charger_manager *info, bool en) { if (info != NULL && info->do_charging) info->do_charging(info, en); return 0; } /* charger_manager ops end */ /* user interface */ struct charger_consumer *charger_manager_get_by_name(struct device *dev, const char *name) { struct charger_consumer *puser; puser = kzalloc(sizeof(struct charger_consumer), GFP_KERNEL); if (puser == NULL) return NULL; mutex_lock(&consumer_mutex); puser->dev = dev; list_add(&puser->list, &consumer_head); if (pinfo != NULL) puser->cm = pinfo; mutex_unlock(&consumer_mutex); return puser; } EXPORT_SYMBOL(charger_manager_get_by_name); int charger_manager_enable_high_voltage_charging( struct charger_consumer *consumer, bool en) { #if defined(CONFIG_BATTERY_SAMSUNG) union power_supply_propval propval = {0, }; propval.intval = !en; psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_FLASH_STATE, propval); #else struct charger_manager *info = consumer->cm; struct list_head *pos = NULL; struct list_head *phead = &consumer_head; struct charger_consumer *ptr = NULL; if (!info) return -EINVAL; pr_debug("[%s] %s, %d\n", __func__, dev_name(consumer->dev), en); if (!en && consumer->hv_charging_disabled == false) consumer->hv_charging_disabled = true; else if (en && consumer->hv_charging_disabled == true) consumer->hv_charging_disabled = false; else { pr_info("[%s] already set: %d %d\n", __func__, consumer->hv_charging_disabled, en); return 0; } mutex_lock(&consumer_mutex); list_for_each(pos, phead) { ptr = container_of(pos, struct charger_consumer, list); if (ptr->hv_charging_disabled == true) { info->enable_hv_charging = false; break; } if (list_is_last(pos, phead)) info->enable_hv_charging = true; } mutex_unlock(&consumer_mutex); pr_info("%s: user: %s, en = %d\n", __func__, dev_name(consumer->dev), info->enable_hv_charging); if (mtk_pe50_get_is_connect(info) && !info->enable_hv_charging) mtk_pe50_stop_algo(info, true); _wake_up_charger(info); #endif return 0; } EXPORT_SYMBOL(charger_manager_enable_high_voltage_charging); int charger_manager_enable_power_path(struct charger_consumer *consumer, int idx, bool en) { #if defined(CONFIG_BATTERY_SAMSUNG) return 0; #else int ret = 0; bool is_en = true; struct charger_manager *info = consumer->cm; struct charger_device *chg_dev = NULL; if (!info) return -EINVAL; switch (idx) { case MAIN_CHARGER: chg_dev = info->chg1_dev; break; case SLAVE_CHARGER: chg_dev = info->chg2_dev; break; default: return -EINVAL; } mutex_lock(&info->pp_lock[idx]); info->enable_pp[idx] = en; if (info->force_disable_pp[idx]) goto out; ret = charger_dev_is_powerpath_enabled(chg_dev, &is_en); if (ret < 0) { chr_err("%s: get is power path enabled failed\n", __func__); goto out; } if (is_en == en) { chr_err("%s: power path is already en = %d\n", __func__, is_en); goto out; } pr_info("%s: enable power path = %d\n", __func__, en); ret = charger_dev_enable_powerpath(chg_dev, en); out: mutex_unlock(&info->pp_lock[idx]); return ret; #endif } int charger_manager_force_disable_power_path(struct charger_consumer *consumer, int idx, bool disable) { #if defined(CONFIG_BATTERY_SAMSUNG) return 0; #else struct charger_manager *info = consumer->cm; struct charger_device *chg_dev = NULL; int ret = 0; if (info == NULL) return -ENODEV; switch (idx) { case MAIN_CHARGER: chg_dev = info->chg1_dev; break; case SLAVE_CHARGER: chg_dev = info->chg2_dev; break; default: ret = -EINVAL; } mutex_lock(&info->pp_lock[idx]); if (disable == info->force_disable_pp[idx]) goto out; info->force_disable_pp[idx] = disable; ret = charger_dev_enable_powerpath(chg_dev, info->force_disable_pp[idx] ? false : info->enable_pp[idx]); out: mutex_unlock(&info->pp_lock[idx]); return ret; #endif } static int _charger_manager_enable_charging(struct charger_consumer *consumer, int idx, bool en) { struct charger_manager *info = consumer->cm; chr_err("%s: dev:%s idx:%d en:%d\n", __func__, dev_name(consumer->dev), idx, en); if (info != NULL) { struct charger_data *pdata = NULL; if (idx == MAIN_CHARGER) pdata = &info->chg1_data; else if (idx == SLAVE_CHARGER) pdata = &info->chg2_data; else return -ENOTSUPP; if (en == false) { _mtk_charger_do_charging(info, en); pdata->disable_charging_count++; } else { if (pdata->disable_charging_count == 1) { _mtk_charger_do_charging(info, en); pdata->disable_charging_count = 0; } else if (pdata->disable_charging_count > 1) pdata->disable_charging_count--; } chr_err("%s: dev:%s idx:%d en:%d cnt:%d\n", __func__, dev_name(consumer->dev), idx, en, pdata->disable_charging_count); return 0; } return -EBUSY; } int charger_manager_enable_charging(struct charger_consumer *consumer, int idx, bool en) { struct charger_manager *info = consumer->cm; int ret = 0; mutex_lock(&info->charger_lock); ret = _charger_manager_enable_charging(consumer, idx, en); mutex_unlock(&info->charger_lock); return ret; } int charger_manager_set_input_current_limit(struct charger_consumer *consumer, int idx, int input_current) { struct charger_manager *info = consumer->cm; if (info != NULL) { struct charger_data *pdata; if (info->data.parallel_vbus) { if (idx == TOTAL_CHARGER) { info->chg1_data.thermal_input_current_limit = input_current; info->chg2_data.thermal_input_current_limit = input_current; } else return -ENOTSUPP; } else { if (idx == MAIN_CHARGER) pdata = &info->chg1_data; else if (idx == SLAVE_CHARGER) pdata = &info->chg2_data; else if (idx == MAIN_DIVIDER_CHARGER) pdata = &info->dvchg1_data; else if (idx == SLAVE_DIVIDER_CHARGER) pdata = &info->dvchg2_data; else return -ENOTSUPP; pdata->thermal_input_current_limit = input_current; } chr_err("%s: dev:%s idx:%d en:%d\n", __func__, dev_name(consumer->dev), idx, input_current); _mtk_charger_change_current_setting(info); _wake_up_charger(info); return 0; } return -EBUSY; } int charger_manager_set_charging_current_limit( struct charger_consumer *consumer, int idx, int charging_current) { struct charger_manager *info = consumer->cm; if (info != NULL) { struct charger_data *pdata; if (idx == MAIN_CHARGER) pdata = &info->chg1_data; else if (idx == SLAVE_CHARGER) pdata = &info->chg2_data; else return -ENOTSUPP; pdata->thermal_charging_current_limit = charging_current; chr_err("%s: dev:%s idx:%d en:%d\n", __func__, dev_name(consumer->dev), idx, charging_current); _mtk_charger_change_current_setting(info); _wake_up_charger(info); return 0; } return -EBUSY; } int charger_manager_get_charger_temperature(struct charger_consumer *consumer, int idx, int *tchg_min, int *tchg_max) { struct charger_manager *info = consumer->cm; if (info != NULL) { struct charger_data *pdata = NULL; if (!upmu_get_rgs_chrdet()) { pr_debug("[%s] No cable in, skip it\n", __func__); *tchg_min = -127; *tchg_max = -127; return -EINVAL; } if (idx == MAIN_CHARGER) pdata = &info->chg1_data; else if (idx == SLAVE_CHARGER) pdata = &info->chg2_data; else if (idx == MAIN_DIVIDER_CHARGER) pdata = &info->dvchg1_data; else if (idx == SLAVE_DIVIDER_CHARGER) pdata = &info->dvchg2_data; else return -ENOTSUPP; *tchg_min = pdata->junction_temp_min; *tchg_max = pdata->junction_temp_max; return 0; } return -EBUSY; } int charger_manager_force_charging_current(struct charger_consumer *consumer, int idx, int charging_current) { struct charger_manager *info = consumer->cm; if (info != NULL) { struct charger_data *pdata = NULL; if (idx == MAIN_CHARGER) pdata = &info->chg1_data; else if (idx == SLAVE_CHARGER) pdata = &info->chg2_data; else return -ENOTSUPP; pdata->force_charging_current = charging_current; _mtk_charger_change_current_setting(info); _wake_up_charger(info); return 0; } return -EBUSY; } int charger_manager_get_current_charging_type(struct charger_consumer *consumer) { struct charger_manager *info = consumer->cm; if (info != NULL) { if (mtk_pe20_get_is_connect(info)) return 2; } return 0; } int charger_manager_get_zcv(struct charger_consumer *consumer, int idx, u32 *uV) { struct charger_manager *info = consumer->cm; int ret = 0; struct charger_device *pchg = NULL; if (info != NULL) { if (idx == MAIN_CHARGER) { pchg = info->chg1_dev; ret = charger_dev_get_zcv(pchg, uV); } else if (idx == SLAVE_CHARGER) { pchg = info->chg2_dev; ret = charger_dev_get_zcv(pchg, uV); } else ret = -1; } else { chr_err("%s info is null\n", __func__); } chr_err("%s zcv:%d ret:%d\n", __func__, *uV, ret); return 0; } int charger_manager_set_constant_voltage(struct charger_consumer *consumer, int idx, u32 uV) { struct charger_manager *info = consumer->cm; int ret = 0; struct charger_device *pchg = NULL; if (info != NULL) { /* battery_cv update*/ info->data.battery_cv = uV; if (idx == MAIN_CHARGER) { pchg = info->chg1_dev; ret = charger_dev_set_constant_voltage(pchg, uV); } else if (idx == SLAVE_CHARGER) { pchg = info->chg2_dev; ret = charger_dev_set_constant_voltage(pchg, uV); } else ret = -1; } else { chr_err("%s info is null\n", __func__); } chr_err("%s charger cv:%d ret:%d\n", __func__, uV, ret); return 0; } int charger_manager_enable_sc(struct charger_consumer *consumer, bool en, int stime, int etime) { struct charger_manager *info = consumer->cm; chr_err("%s en:%d %d %d\n", __func__, en, stime, etime); info->sc.start_time = stime; info->sc.end_time = etime; info->sc.enable = en; _wake_up_charger(info); return 0; } int charger_manager_set_sc_current_limit(struct charger_consumer *consumer, int cl) { struct charger_manager *info = consumer->cm; chr_err("%s %d\n", __func__, cl); info->sc.current_limit = cl; _wake_up_charger(info); return 0; } int charger_manager_set_bh(struct charger_consumer *consumer, int bh) { struct charger_manager *info = consumer->cm; chr_err("%s %d\n", __func__, bh); info->sc.bh = bh; _wake_up_charger(info); return 0; } int charger_manager_enable_chg_type_det(struct charger_consumer *consumer, bool en) { struct charger_manager *info = consumer->cm; struct charger_device *chg_dev = NULL; int ret = 0; if (info != NULL) { switch (info->data.bc12_charger) { case MAIN_CHARGER: chg_dev = info->chg1_dev; break; case SLAVE_CHARGER: chg_dev = info->chg2_dev; break; default: chg_dev = info->chg1_dev; chr_err("%s: invalid number, use main charger as default\n", __func__); break; } chr_err("%s: chg%d is doing bc12\n", __func__, info->data.bc12_charger + 1); ret = charger_dev_enable_chg_type_det(chg_dev, en); if (ret < 0) { chr_err("%s: en chgdet fail, en = %d\n", __func__, en); return ret; } } else chr_err("%s: charger_manager is null\n", __func__); return 0; } int register_charger_manager_notifier(struct charger_consumer *consumer, struct notifier_block *nb) { int ret = 0; struct charger_manager *info = consumer->cm; mutex_lock(&consumer_mutex); if (info != NULL) ret = srcu_notifier_chain_register(&info->evt_nh, nb); else consumer->pnb = nb; mutex_unlock(&consumer_mutex); return ret; } int unregister_charger_manager_notifier(struct charger_consumer *consumer, struct notifier_block *nb) { int ret = 0; struct charger_manager *info = consumer->cm; mutex_lock(&consumer_mutex); if (info != NULL) ret = srcu_notifier_chain_unregister(&info->evt_nh, nb); else consumer->pnb = NULL; mutex_unlock(&consumer_mutex); return ret; } /* user interface end*/ /* factory mode */ #define CHARGER_DEVNAME "charger_ftm" #define GET_IS_SLAVE_CHARGER_EXIST _IOW('k', 13, int) static struct class *charger_class; static struct cdev *charger_cdev; static int charger_major; static dev_t charger_devno; static int is_slave_charger_exist(void) { if (get_charger_by_name("secondary_chg") == NULL) return 0; return 1; } static long charger_ftm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; int out_data = 0; void __user *user_data = (void __user *)arg; switch (cmd) { case GET_IS_SLAVE_CHARGER_EXIST: out_data = is_slave_charger_exist(); ret = copy_to_user(user_data, &out_data, sizeof(out_data)); chr_err("[%s] SLAVE_CHARGER_EXIST: %d\n", __func__, out_data); break; default: chr_err("[%s] Error ID\n", __func__); break; } return ret; } #ifdef CONFIG_COMPAT static long charger_ftm_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; switch (cmd) { case GET_IS_SLAVE_CHARGER_EXIST: ret = file->f_op->unlocked_ioctl(file, cmd, arg); break; default: chr_err("[%s] Error ID\n", __func__); break; } return ret; } #endif static int charger_ftm_open(struct inode *inode, struct file *file) { return 0; } static int charger_ftm_release(struct inode *inode, struct file *file) { return 0; } static const struct file_operations charger_ftm_fops = { .owner = THIS_MODULE, .unlocked_ioctl = charger_ftm_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = charger_ftm_compat_ioctl, #endif .open = charger_ftm_open, .release = charger_ftm_release, }; void charger_ftm_init(void) { struct class_device *class_dev = NULL; int ret = 0; ret = alloc_chrdev_region(&charger_devno, 0, 1, CHARGER_DEVNAME); if (ret < 0) { chr_err("[%s]Can't get major num for charger_ftm\n", __func__); return; } charger_cdev = cdev_alloc(); if (!charger_cdev) { chr_err("[%s]cdev_alloc fail\n", __func__); goto unregister; } charger_cdev->owner = THIS_MODULE; charger_cdev->ops = &charger_ftm_fops; ret = cdev_add(charger_cdev, charger_devno, 1); if (ret < 0) { chr_err("[%s] cdev_add failed\n", __func__); goto free_cdev; } charger_major = MAJOR(charger_devno); charger_class = class_create(THIS_MODULE, CHARGER_DEVNAME); if (IS_ERR(charger_class)) { chr_err("[%s] class_create failed\n", __func__); goto free_cdev; } class_dev = (struct class_device *)device_create(charger_class, NULL, charger_devno, NULL, CHARGER_DEVNAME); if (IS_ERR(class_dev)) { chr_err("[%s] device_create failed\n", __func__); goto free_class; } pr_debug("%s done\n", __func__); return; free_class: class_destroy(charger_class); free_cdev: cdev_del(charger_cdev); unregister: unregister_chrdev_region(charger_devno, 1); } /* factory mode end */ void mtk_charger_get_atm_mode(struct charger_manager *info) { char atm_str[64] = {0}; char *ptr = NULL, *ptr_e = NULL; char keyword[] = "androidboot.atm="; int size = 0; info->atm_enabled = false; ptr = strstr(saved_command_line, keyword); if (ptr != 0) { ptr_e = strstr(ptr, " "); if (ptr_e == NULL) goto end; size = ptr_e - (ptr + strlen(keyword)); if (size <= 0) goto end; strncpy(atm_str, ptr + strlen(keyword), size); atm_str[size] = '\0'; if (!strncmp(atm_str, "enable", strlen("enable"))) info->atm_enabled = true; } end: pr_info("%s: atm_enabled = %d\n", __func__, info->atm_enabled); } /* internal algorithm common function */ bool is_dual_charger_supported(struct charger_manager *info) { if (info->chg2_dev == NULL) return false; return true; } int charger_enable_vbus_ovp(struct charger_manager *pinfo, bool enable) { int ret = 0; u32 sw_ovp = 0; if (enable) sw_ovp = pinfo->data.max_charger_voltage_setting; else sw_ovp = 15000000; /* Enable/Disable SW OVP status */ pinfo->data.max_charger_voltage = sw_ovp; chr_err("[%s] en:%d ovp:%d\n", __func__, enable, sw_ovp); return ret; } bool is_typec_adapter(struct charger_manager *info) { int rp; rp = adapter_dev_get_property(info->pd_adapter, TYPEC_RP_LEVEL); if (info->pd_type == MTK_PD_CONNECT_TYPEC_ONLY_SNK && rp != 500 && info->chr_type != STANDARD_HOST && info->chr_type != CHARGING_HOST && mtk_pe20_get_is_connect(info) == false && mtk_pe_get_is_connect(info) == false && info->enable_type_c == true) return true; return false; } int charger_get_vbus(void) { int ret = 0; int vchr = 0; if (pinfo == NULL) return 0; ret = charger_dev_get_vbus(pinfo->chg1_dev, &vchr); if (ret < 0) { chr_err("%s: get vbus failed: %d\n", __func__, ret); return ret; } vchr = vchr / 1000; return vchr; } /* internal algorithm common function end */ /* sw jeita */ void sw_jeita_state_machine_init(struct charger_manager *info) { struct sw_jeita_data *sw_jeita; if (info->enable_sw_jeita == true) { sw_jeita = &info->sw_jeita; info->battery_temp = battery_get_bat_temperature(); if (info->battery_temp >= info->data.temp_t4_thres) sw_jeita->sm = TEMP_ABOVE_T4; else if (info->battery_temp > info->data.temp_t3_thres) sw_jeita->sm = TEMP_T3_TO_T4; else if (info->battery_temp >= info->data.temp_t2_thres) sw_jeita->sm = TEMP_T2_TO_T3; else if (info->battery_temp >= info->data.temp_t1_thres) sw_jeita->sm = TEMP_T1_TO_T2; else if (info->battery_temp >= info->data.temp_t0_thres) sw_jeita->sm = TEMP_T0_TO_T1; else sw_jeita->sm = TEMP_BELOW_T0; chr_err("[SW_JEITA] tmp:%d sm:%d\n", info->battery_temp, sw_jeita->sm); } } void do_sw_jeita_state_machine(struct charger_manager *info) { struct sw_jeita_data *sw_jeita; sw_jeita = &info->sw_jeita; sw_jeita->pre_sm = sw_jeita->sm; sw_jeita->charging = true; /* JEITA battery temp Standard */ if (info->battery_temp >= info->data.temp_t4_thres) { chr_err("[SW_JEITA] Battery Over high Temperature(%d) !!\n", info->data.temp_t4_thres); sw_jeita->sm = TEMP_ABOVE_T4; sw_jeita->charging = false; } else if (info->battery_temp > info->data.temp_t3_thres) { /* control 45 degree to normal behavior */ if ((sw_jeita->sm == TEMP_ABOVE_T4) && (info->battery_temp >= info->data.temp_t4_thres_minus_x_degree)) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t4_thres_minus_x_degree, info->data.temp_t4_thres); sw_jeita->charging = false; } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t3_thres, info->data.temp_t4_thres); sw_jeita->sm = TEMP_T3_TO_T4; } } else if (info->battery_temp >= info->data.temp_t2_thres) { if (((sw_jeita->sm == TEMP_T3_TO_T4) && (info->battery_temp >= info->data.temp_t3_thres_minus_x_degree)) || ((sw_jeita->sm == TEMP_T1_TO_T2) && (info->battery_temp <= info->data.temp_t2_thres_plus_x_degree))) { chr_err("[SW_JEITA] Battery Temperature not recovery to normal temperature charging mode yet!!\n"); } else { chr_err("[SW_JEITA] Battery Normal Temperature between %d and %d !!\n", info->data.temp_t2_thres, info->data.temp_t3_thres); sw_jeita->sm = TEMP_T2_TO_T3; } } else if (info->battery_temp >= info->data.temp_t1_thres) { if ((sw_jeita->sm == TEMP_T0_TO_T1 || sw_jeita->sm == TEMP_BELOW_T0) && (info->battery_temp <= info->data.temp_t1_thres_plus_x_degree)) { if (sw_jeita->sm == TEMP_T0_TO_T1) { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t1_thres_plus_x_degree, info->data.temp_t2_thres); } if (sw_jeita->sm == TEMP_BELOW_T0) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t1_thres, info->data.temp_t1_thres_plus_x_degree); sw_jeita->charging = false; } } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t1_thres, info->data.temp_t2_thres); sw_jeita->sm = TEMP_T1_TO_T2; } } else if (info->battery_temp >= info->data.temp_t0_thres) { if ((sw_jeita->sm == TEMP_BELOW_T0) && (info->battery_temp <= info->data.temp_t0_thres_plus_x_degree)) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t0_thres, info->data.temp_t0_thres_plus_x_degree); sw_jeita->charging = false; } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t0_thres, info->data.temp_t1_thres); sw_jeita->sm = TEMP_T0_TO_T1; } } else { chr_err("[SW_JEITA] Battery below low Temperature(%d) !!\n", info->data.temp_t0_thres); sw_jeita->sm = TEMP_BELOW_T0; sw_jeita->charging = false; } /* set CV after temperature changed */ /* In normal range, we adjust CV dynamically */ if (sw_jeita->sm != TEMP_T2_TO_T3) { if (sw_jeita->sm == TEMP_ABOVE_T4) sw_jeita->cv = info->data.jeita_temp_above_t4_cv; else if (sw_jeita->sm == TEMP_T3_TO_T4) sw_jeita->cv = info->data.jeita_temp_t3_to_t4_cv; else if (sw_jeita->sm == TEMP_T2_TO_T3) sw_jeita->cv = 0; else if (sw_jeita->sm == TEMP_T1_TO_T2) sw_jeita->cv = info->data.jeita_temp_t1_to_t2_cv; else if (sw_jeita->sm == TEMP_T0_TO_T1) sw_jeita->cv = info->data.jeita_temp_t0_to_t1_cv; else if (sw_jeita->sm == TEMP_BELOW_T0) sw_jeita->cv = info->data.jeita_temp_below_t0_cv; else sw_jeita->cv = info->data.battery_cv; } else { sw_jeita->cv = 0; } chr_err("[SW_JEITA]preState:%d newState:%d tmp:%d cv:%d\n", sw_jeita->pre_sm, sw_jeita->sm, info->battery_temp, sw_jeita->cv); } static ssize_t show_sw_jeita(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->enable_sw_jeita); return sprintf(buf, "%d\n", pinfo->enable_sw_jeita); } static ssize_t store_sw_jeita(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp == 0) pinfo->enable_sw_jeita = false; else { pinfo->enable_sw_jeita = true; sw_jeita_state_machine_init(pinfo); } } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR(sw_jeita, 0644, show_sw_jeita, store_sw_jeita); /* sw jeita end*/ /* pump express series */ bool mtk_is_pep_series_connect(struct charger_manager *info) { if (mtk_pe20_get_is_connect(info) || mtk_pe_get_is_connect(info)) return true; return false; } static ssize_t show_pe20(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->enable_pe_2); return sprintf(buf, "%d\n", pinfo->enable_pe_2); } static ssize_t store_pe20(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp == 0) pinfo->enable_pe_2 = false; else pinfo->enable_pe_2 = true; } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR(pe20, 0644, show_pe20, store_pe20); static ssize_t show_pe40(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->enable_pe_4); return sprintf(buf, "%d\n", pinfo->enable_pe_4); } static ssize_t store_pe40(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp == 0) pinfo->enable_pe_4 = false; else pinfo->enable_pe_4 = true; } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR(pe40, 0644, show_pe40, store_pe40); /* pump express series end*/ static ssize_t show_charger_log_level(struct device *dev, struct device_attribute *attr, char *buf) { chr_err("%s: %d\n", __func__, chargerlog_level); return sprintf(buf, "%d\n", chargerlog_level); } static ssize_t store_charger_log_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret = 0; chr_err("%s\n", __func__); if (buf != NULL && size != 0) { chr_err("%s: buf is %s\n", __func__, buf); ret = kstrtoul(buf, 10, &val); if (ret < 0) { chr_err("%s: kstrtoul fail, ret = %d\n", __func__, ret); return ret; } if (val < 0) { chr_err("%s: val is inavlid: %ld\n", __func__, val); val = 0; } chargerlog_level = val; chr_err("%s: log_level=%d\n", __func__, chargerlog_level); } return size; } static DEVICE_ATTR(charger_log_level, 0644, show_charger_log_level, store_charger_log_level); static ssize_t show_pdc_max_watt_level(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; return sprintf(buf, "%d\n", mtk_pdc_get_max_watt(pinfo)); } static ssize_t store_pdc_max_watt_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; int temp; if (kstrtoint(buf, 10, &temp) == 0) { mtk_pdc_set_max_watt(pinfo, temp); chr_err("[store_pdc_max_watt]:%d\n", temp); } else chr_err("[store_pdc_max_watt]: format error!\n"); return size; } static DEVICE_ATTR(pdc_max_watt, 0644, show_pdc_max_watt_level, store_pdc_max_watt_level); int mtk_get_dynamic_cv(struct charger_manager *info, unsigned int *cv) { int ret = 0; u32 _cv, _cv_temp; unsigned int vbat_threshold[4] = {3400000, 0, 0, 0}; u32 vbat_bif = 0, vbat_auxadc = 0, vbat = 0; u32 retry_cnt = 0; if (pmic_is_bif_exist()) { do { vbat_auxadc = battery_get_bat_voltage() * 1000; ret = pmic_get_bif_battery_voltage(&vbat_bif); vbat_bif = vbat_bif * 1000; if (ret >= 0 && vbat_bif != 0 && vbat_bif < vbat_auxadc) { vbat = vbat_bif; chr_err("%s: use BIF vbat = %duV, dV to auxadc = %duV\n", __func__, vbat, vbat_auxadc - vbat_bif); break; } retry_cnt++; } while (retry_cnt < 5); if (retry_cnt == 5) { ret = 0; vbat = vbat_auxadc; chr_err("%s: use AUXADC vbat = %duV, since BIF vbat = %duV\n", __func__, vbat_auxadc, vbat_bif); } /* Adjust CV according to the obtained vbat */ vbat_threshold[1] = info->data.bif_threshold1; vbat_threshold[2] = info->data.bif_threshold2; _cv_temp = info->data.bif_cv_under_threshold2; if (!info->enable_dynamic_cv && vbat >= vbat_threshold[2]) { _cv = info->data.battery_cv; goto out; } if (vbat < vbat_threshold[1]) _cv = 4608000; else if (vbat >= vbat_threshold[1] && vbat < vbat_threshold[2]) _cv = _cv_temp; else { _cv = info->data.battery_cv; info->enable_dynamic_cv = false; } out: *cv = _cv; chr_err("%s: CV = %duV, enable_dynamic_cv = %d\n", __func__, _cv, info->enable_dynamic_cv); } else ret = -ENOTSUPP; return ret; } int charger_manager_notifier(struct charger_manager *info, int event) { return srcu_notifier_call_chain(&info->evt_nh, event, NULL); } int charger_psy_event(struct notifier_block *nb, unsigned long event, void *v) { #if !defined(CONFIG_BATTERY_SAMSUNG) struct charger_manager *info = container_of(nb, struct charger_manager, psy_nb); struct power_supply *psy = v; union power_supply_propval val; int ret; int tmp = 0; if (strcmp(psy->desc->name, "battery") == 0) { ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); if (!ret) { tmp = val.intval / 10; if (info->battery_temp != tmp && mt_get_charger_type() != CHARGER_UNKNOWN) { _wake_up_charger(info); chr_err("%s: %ld %s tmp:%d %d chr:%d\n", __func__, event, psy->desc->name, tmp, info->battery_temp, mt_get_charger_type()); } } } #endif return NOTIFY_DONE; } void mtk_charger_int_handler(void) { chr_err("%s\n", __func__); if (pinfo == NULL) { chr_err("charger is not rdy ,skip1\n"); return; } if (pinfo->init_done != true) { chr_err("charger is not rdy ,skip2\n"); return; } if (mt_get_charger_type() == CHARGER_UNKNOWN) { mutex_lock(&pinfo->cable_out_lock); pinfo->cable_out_cnt++; chr_err("cable_out_cnt=%d\n", pinfo->cable_out_cnt); mutex_unlock(&pinfo->cable_out_lock); charger_manager_notifier(pinfo, CHARGER_NOTIFY_STOP_CHARGING); } else charger_manager_notifier(pinfo, CHARGER_NOTIFY_START_CHARGING); chr_err("wake_up_charger\n"); _wake_up_charger(pinfo); } static int mtk_charger_plug_in(struct charger_manager *info, enum charger_type chr_type) { info->chr_type = chr_type; info->charger_thread_polling = true; info->can_charging = true; info->enable_dynamic_cv = true; info->safety_timeout = false; info->vbusov_stat = false; chr_err("mtk_is_charger_on plug in, type:%d\n", chr_type); if (info->plug_in != NULL) info->plug_in(info); memset(&pinfo->sc.data, 0, sizeof(struct scd_cmd_param_t_1)); pinfo->sc.disable_in_this_plug = false; wakeup_sc_algo_cmd(&pinfo->sc.data, SC_EVENT_PLUG_IN, 0); #if !defined(CONFIG_BATTERY_SAMSUNG) charger_dev_set_input_current(info->chg1_dev, info->chg1_data.input_current_limit); #endif charger_dev_plug_in(info->chg1_dev); return 0; } static int mtk_charger_plug_out(struct charger_manager *info) { struct charger_data *pdata1 = &info->chg1_data; struct charger_data *pdata2 = &info->chg2_data; chr_err("%s\n", __func__); info->chr_type = CHARGER_UNKNOWN; info->charger_thread_polling = false; info->pd_reset = false; pdata1->disable_charging_count = 0; pdata1->input_current_limit_by_aicl = -1; pdata2->disable_charging_count = 0; if (info->plug_out != NULL) info->plug_out(info); memset(&pinfo->sc.data, 0, sizeof(struct scd_cmd_param_t_1)); wakeup_sc_algo_cmd(&pinfo->sc.data, SC_EVENT_PLUG_OUT, 0); #if !defined(CONFIG_BATTERY_SAMSUNG) charger_dev_set_input_current(info->chg1_dev, 100000); #endif charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage); charger_dev_plug_out(info->chg1_dev); return 0; } static bool mtk_is_charger_on(struct charger_manager *info) { enum charger_type chr_type; chr_type = mt_get_charger_type(); if (chr_type == CHARGER_UNKNOWN) { if (info->chr_type != CHARGER_UNKNOWN) { mtk_charger_plug_out(info); mutex_lock(&info->cable_out_lock); info->cable_out_cnt = 0; mutex_unlock(&info->cable_out_lock); } } else { if (info->chr_type == CHARGER_UNKNOWN) mtk_charger_plug_in(info, chr_type); else info->chr_type = chr_type; if (info->cable_out_cnt > 0) { mtk_charger_plug_out(info); mtk_charger_plug_in(info, chr_type); mutex_lock(&info->cable_out_lock); info->cable_out_cnt--; mutex_unlock(&info->cable_out_lock); } } if (chr_type == CHARGER_UNKNOWN) return false; return true; } static void charger_update_data(struct charger_manager *info) { info->battery_temp = battery_get_bat_temperature(); } static int mtk_chgstat_notify(struct charger_manager *info) { int ret = 0; char *env[2] = { "CHGSTAT=1", NULL }; chr_err("%s: 0x%x\n", __func__, info->notify_code); ret = kobject_uevent_env(&info->pdev->dev.kobj, KOBJ_CHANGE, env); if (ret) chr_err("%s: kobject_uevent_fail, ret=%d", __func__, ret); return ret; } /* return false if vbus is over max_charger_voltage */ static bool mtk_chg_check_vbus(struct charger_manager *info) { int vchr = 0; vchr = battery_get_vbus() * 1000; /* uV */ if (vchr > info->data.max_charger_voltage) { chr_err("%s: vbus(%d mV) > %d mV\n", __func__, vchr / 1000, info->data.max_charger_voltage / 1000); return false; } return true; } static void mtk_battery_notify_VCharger_check(struct charger_manager *info) { #if defined(BATTERY_NOTIFY_CASE_0001_VCHARGER) int vchr = 0; vchr = battery_get_vbus() * 1000; /* uV */ if (vchr < info->data.max_charger_voltage) info->notify_code &= ~CHG_VBUS_OV_STATUS; else { info->notify_code |= CHG_VBUS_OV_STATUS; chr_err("[BATTERY] charger_vol(%d mV) > %d mV\n", vchr / 1000, info->data.max_charger_voltage / 1000); mtk_chgstat_notify(info); } #endif } static void mtk_battery_notify_VBatTemp_check(struct charger_manager *info) { #if defined(BATTERY_NOTIFY_CASE_0002_VBATTEMP) if (info->battery_temp >= info->thermal.max_charge_temp) { info->notify_code |= CHG_BAT_OT_STATUS; chr_err("[BATTERY] bat_temp(%d) out of range(too high)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_OT_STATUS; } if (info->enable_sw_jeita == true) { if (info->battery_temp < info->data.temp_neg_10_thres) { info->notify_code |= CHG_BAT_LT_STATUS; chr_err("bat_temp(%d) out of range(too low)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_LT_STATUS; } } else { #ifdef BAT_LOW_TEMP_PROTECT_ENABLE if (info->battery_temp < info->thermal.min_charge_temp) { info->notify_code |= CHG_BAT_LT_STATUS; chr_err("bat_temp(%d) out of range(too low)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_LT_STATUS; } #endif } #endif } static void mtk_battery_notify_UI_test(struct charger_manager *info) { switch (info->notify_test_mode) { case 1: info->notify_code = CHG_VBUS_OV_STATUS; pr_debug("[%s] CASE_0001_VCHARGER\n", __func__); break; case 2: info->notify_code = CHG_BAT_OT_STATUS; pr_debug("[%s] CASE_0002_VBATTEMP\n", __func__); break; case 3: info->notify_code = CHG_OC_STATUS; pr_debug("[%s] CASE_0003_ICHARGING\n", __func__); break; case 4: info->notify_code = CHG_BAT_OV_STATUS; pr_debug("[%s] CASE_0004_VBAT\n", __func__); break; case 5: info->notify_code = CHG_ST_TMO_STATUS; pr_debug("[%s] CASE_0005_TOTAL_CHARGINGTIME\n", __func__); break; case 6: info->notify_code = CHG_BAT_LT_STATUS; pr_debug("[%s] CASE6: VBATTEMP_LOW\n", __func__); break; case 7: info->notify_code = CHG_TYPEC_WD_STATUS; pr_debug("[%s] CASE7: Moisture Detection\n", __func__); break; default: pr_debug("[%s] Unknown BN_TestMode Code: %x\n", __func__, info->notify_test_mode); } mtk_chgstat_notify(info); } static void mtk_battery_notify_check(struct charger_manager *info) { if (info->notify_test_mode == 0x0000) { mtk_battery_notify_VCharger_check(info); mtk_battery_notify_VBatTemp_check(info); } else { mtk_battery_notify_UI_test(info); } } static void check_battery_exist(struct charger_manager *info) { struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; unsigned int i = 0; int count = 0; int boot_mode = 11;//UNKNOWN_BOOT // workaround for mt6768 //int boot_mode = get_boot_mode(); dev = &(info->pdev->dev); if (dev != NULL){ boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node){ chr_err("%s: failed to get boot mode phandle\n", __func__); return; } else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag){ chr_err("%s: failed to get atag,boot\n", __func__); return; } else boot_mode = tag->bootmode; } } if (is_disable_charger()) return; for (i = 0; i < 3; i++) { if (pmic_is_battery_exist() == false) count++; } if (count >= 3) { if (boot_mode == META_BOOT || boot_mode == ADVMETA_BOOT || boot_mode == ATE_FACTORY_BOOT) chr_info("boot_mode = %d, bypass battery check\n", boot_mode); else { chr_err("battery doesn't exist, shutdown\n"); orderly_poweroff(true); } } } static void check_dynamic_mivr(struct charger_manager *info) { int vbat = 0; if (info->enable_dynamic_mivr) { if (!mtk_pe40_get_is_connect(info) && !mtk_pe20_get_is_connect(info) && !mtk_pe_get_is_connect(info) && !mtk_pdc_check_charger(info)) { vbat = battery_get_bat_voltage(); if (vbat < info->data.min_charger_voltage_2 / 1000 - 200) charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage_2); else if (vbat < info->data.min_charger_voltage_1 / 1000 - 200) charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage_1); else charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage); } } } static void mtk_chg_get_tchg(struct charger_manager *info) { int ret; int tchg_min = -127, tchg_max = -127; struct charger_data *pdata; bool en = false; pdata = &info->chg1_data; ret = charger_dev_get_temperature(info->chg1_dev, &tchg_min, &tchg_max); if (ret < 0) { pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; } else { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } if (is_slave_charger_exist()) { pdata = &info->chg2_data; ret = charger_dev_get_temperature(info->chg2_dev, &tchg_min, &tchg_max); if (ret < 0) { pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; } else { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } if (info->dvchg1_dev) { pdata = &info->dvchg1_data; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->dvchg1_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->dvchg1_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } if (info->dvchg2_dev) { pdata = &info->dvchg2_data; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->dvchg2_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->dvchg2_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } } static void charger_check_status(struct charger_manager *info) { bool charging = true; int temperature = 0; struct battery_thermal_protection_data *thermal = NULL; if (mt_get_charger_type() == CHARGER_UNKNOWN) return; temperature = info->battery_temp; thermal = &info->thermal; if (info->enable_sw_jeita == true) { do_sw_jeita_state_machine(info); if (info->sw_jeita.charging == false) { charging = false; goto stop_charging; } } else { if (thermal->enable_min_charge_temp) { if (temperature < thermal->min_charge_temp) { chr_err("Battery Under Temperature or NTC fail %d %d\n", temperature, thermal->min_charge_temp); thermal->sm = BAT_TEMP_LOW; charging = false; goto stop_charging; } else if (thermal->sm == BAT_TEMP_LOW) { if (temperature >= thermal->min_charge_temp_plus_x_degree) { chr_err("Battery Temperature raise from %d to %d(%d), allow charging!!\n", thermal->min_charge_temp, temperature, thermal->min_charge_temp_plus_x_degree); thermal->sm = BAT_TEMP_NORMAL; } else { charging = false; goto stop_charging; } } } if (temperature >= thermal->max_charge_temp) { chr_err("Battery over Temperature or NTC fail %d %d\n", temperature, thermal->max_charge_temp); thermal->sm = BAT_TEMP_HIGH; charging = false; goto stop_charging; } else if (thermal->sm == BAT_TEMP_HIGH) { if (temperature < thermal->max_charge_temp_minus_x_degree) { chr_err("Battery Temperature raise from %d to %d(%d), allow charging!!\n", thermal->max_charge_temp, temperature, thermal->max_charge_temp_minus_x_degree); thermal->sm = BAT_TEMP_NORMAL; } else { charging = false; goto stop_charging; } } } mtk_chg_get_tchg(info); if (!mtk_chg_check_vbus(info)) { charging = false; goto stop_charging; } if (info->cmd_discharging) charging = false; if (info->safety_timeout) charging = false; if (info->vbusov_stat) charging = false; if (info->sc.disable_charger == true) charging = false; stop_charging: mtk_battery_notify_check(info); chr_err("tmp:%d (jeita:%d sm:%d cv:%d en:%d) (sm:%d) en:%d c:%d s:%d ov:%d sc:%d %d %d\n", temperature, info->enable_sw_jeita, info->sw_jeita.sm, info->sw_jeita.cv, info->sw_jeita.charging, thermal->sm, charging, info->cmd_discharging, info->safety_timeout, info->vbusov_stat, info->sc.disable_charger, info->can_charging, charging); if (charging != info->can_charging) _charger_manager_enable_charging(info->chg1_consumer, 0, charging); info->can_charging = charging; } static void kpoc_power_off_check(struct charger_manager *info) { #if !defined(CONFIG_BATTERY_SAMSUNG) int vbus = 0; struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT // workaround for mt6768 //int boot_mode = get_boot_mode(); dev = &(info->pdev->dev); if (dev != NULL){ boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node){ chr_err("%s: failed to get boot mode phandle\n", __func__); } else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag){ chr_err("%s: failed to get atag,boot\n", __func__); } else boot_mode = tag->bootmode; } } if (boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT || boot_mode == LOW_POWER_OFF_CHARGING_BOOT) { if (atomic_read(&info->enable_kpoc_shdn)) { vbus = battery_get_vbus(); if (vbus >= 0 && vbus < 2500 && !mt_charger_plugin()) { chr_err("Unplug Charger/USB in KPOC mode, shutdown\n"); chr_err("%s: system_state=%d\n", __func__, system_state); if (system_state != SYSTEM_POWER_OFF) kernel_power_off(); } } } #endif } #ifdef CONFIG_PM static int charger_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { struct timespec now; switch (pm_event) { case PM_SUSPEND_PREPARE: pinfo->is_suspend = true; chr_debug("%s: enter PM_SUSPEND_PREPARE\n", __func__); break; case PM_POST_SUSPEND: pinfo->is_suspend = false; chr_debug("%s: enter PM_POST_SUSPEND\n", __func__); get_monotonic_boottime(&now); if (timespec_compare(&now, &pinfo->endtime) >= 0 && pinfo->endtime.tv_sec != 0 && pinfo->endtime.tv_nsec != 0) { chr_err("%s: alarm timeout, wake up charger\n", __func__); pinfo->endtime.tv_sec = 0; pinfo->endtime.tv_nsec = 0; _wake_up_charger(pinfo); } break; default: break; } return NOTIFY_DONE; } static struct notifier_block charger_pm_notifier_func = { .notifier_call = charger_pm_event, .priority = 0, }; #endif /* CONFIG_PM */ static enum alarmtimer_restart mtk_charger_alarm_timer_func(struct alarm *alarm, ktime_t now) { struct charger_manager *info = container_of(alarm, struct charger_manager, charger_timer); unsigned long flags; if (info->is_suspend == false) { chr_err("%s: not suspend, wake up charger\n", __func__); _wake_up_charger(info); } else { chr_err("%s: alarm timer timeout\n", __func__); spin_lock_irqsave(&info->slock, flags); if (!info->charger_wakelock->active) __pm_stay_awake(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); } return ALARMTIMER_NORESTART; } static void mtk_charger_start_timer(struct charger_manager *info) { struct timespec time, time_now; ktime_t ktime; int ret = 0; /* If the timer was already set, cancel it */ ret = alarm_try_to_cancel(&pinfo->charger_timer); if (ret < 0) { chr_err("%s: callback was running, skip timer\n", __func__); return; } get_monotonic_boottime(&time_now); #if defined(CONFIG_SEC_FACTORY) if (f_mode_battery == OB_MODE) info->polling_interval = 60; #endif time.tv_sec = info->polling_interval; time.tv_nsec = 0; info->endtime = timespec_add(time_now, time); ktime = ktime_set(info->endtime.tv_sec, info->endtime.tv_nsec); chr_err("%s: alarm timer start:%d, %ld %ld polling_interval(%d) f_mode_battery(%d)\n", __func__, ret, info->endtime.tv_sec, info->endtime.tv_nsec, info->polling_interval, f_mode_battery); alarm_start(&pinfo->charger_timer, ktime); } static void mtk_charger_init_timer(struct charger_manager *info) { alarm_init(&info->charger_timer, ALARM_BOOTTIME, mtk_charger_alarm_timer_func); mtk_charger_start_timer(info); #ifdef CONFIG_PM if (register_pm_notifier(&charger_pm_notifier_func)) chr_err("%s: register pm failed\n", __func__); #endif /* CONFIG_PM */ } static int charger_routine_thread(void *arg) { struct charger_manager *info = arg; unsigned long flags = 0; bool is_charger_on = false; int bat_current = 0, chg_current = 0; int ret; while (1) { ret = wait_event_interruptible(info->wait_que, (info->charger_thread_timeout == true)); if (ret < 0) { chr_err("%s: wait event been interrupted(%d)\n", __func__, ret); continue; } mutex_lock(&info->charger_lock); spin_lock_irqsave(&info->slock, flags); if (!info->charger_wakelock->active) __pm_stay_awake(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); info->charger_thread_timeout = false; bat_current = battery_get_bat_current(); chg_current = pmic_get_charging_current(); chr_err("Vbat=%d,Ibat=%d,I=%d,VChr=%d,T=%d,Soc=%d:%d,CT:%d:%d hv:%d pd:%d:%d\n", battery_get_bat_voltage(), bat_current, chg_current, battery_get_vbus(), battery_get_bat_temperature(), battery_get_soc(), battery_get_uisoc(), mt_get_charger_type(), info->chr_type, info->enable_hv_charging, info->pd_type, info->pd_reset); is_charger_on = mtk_is_charger_on(info); if (info->charger_thread_polling == true) mtk_charger_start_timer(info); charger_update_data(info); check_battery_exist(info); check_dynamic_mivr(info); charger_check_status(info); kpoc_power_off_check(info); if (is_disable_charger() == false) { if (is_charger_on == true) { if (info->do_algorithm) info->do_algorithm(info); wakeup_sc_algo_cmd(&pinfo->sc.data, SC_EVENT_CHARGING, 0); } else wakeup_sc_algo_cmd(&pinfo->sc.data, SC_EVENT_STOP_CHARGING, 0); } else chr_debug("disable charging\n"); spin_lock_irqsave(&info->slock, flags); __pm_relax(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); chr_debug("%s end , %d\n", __func__, info->charger_thread_timeout); mutex_unlock(&info->charger_lock); } return 0; } static int mtk_charger_parse_dt(struct charger_manager *info, struct device *dev) { struct device_node *np = dev->of_node; u32 val; chr_err("%s: starts\n", __func__); if (!np) { chr_err("%s: no device node\n", __func__); return -EINVAL; } if (of_property_read_string(np, "algorithm_name", &info->algorithm_name) < 0) { chr_err("%s: no algorithm_name name\n", __func__); info->algorithm_name = "SwitchCharging"; } #if !defined(CONFIG_BATTERY_SAMSUNG) if (strcmp(info->algorithm_name, "SwitchCharging") == 0) { chr_err("found SwitchCharging\n"); mtk_switch_charging_init(info); } #endif #ifdef CONFIG_MTK_DUAL_CHARGER_SUPPORT if (strcmp(info->algorithm_name, "DualSwitchCharging") == 0) { pr_debug("found DualSwitchCharging\n"); mtk_dual_switch_charging_init(info); } #endif info->disable_charger = of_property_read_bool(np, "disable_charger"); info->enable_sw_safety_timer = of_property_read_bool(np, "enable_sw_safety_timer"); info->sw_safety_timer_setting = info->enable_sw_safety_timer; info->enable_sw_jeita = of_property_read_bool(np, "enable_sw_jeita"); info->enable_pe_plus = of_property_read_bool(np, "enable_pe_plus"); info->enable_pe_2 = of_property_read_bool(np, "enable_pe_2"); info->enable_pe_4 = of_property_read_bool(np, "enable_pe_4"); info->enable_pe_5 = of_property_read_bool(np, "enable_pe_5"); info->enable_type_c = of_property_read_bool(np, "enable_type_c"); info->enable_dynamic_mivr = of_property_read_bool(np, "enable_dynamic_mivr"); info->disable_pd_dual = of_property_read_bool(np, "disable_pd_dual"); info->enable_hv_charging = true; /* common */ if (of_property_read_u32(np, "battery_cv", &val) >= 0) info->data.battery_cv = val; else { chr_err("use default BATTERY_CV:%d\n", BATTERY_CV); info->data.battery_cv = BATTERY_CV; } if (of_property_read_u32(np, "max_charger_voltage", &val) >= 0) info->data.max_charger_voltage = val; else { chr_err("use default V_CHARGER_MAX:%d\n", V_CHARGER_MAX); info->data.max_charger_voltage = V_CHARGER_MAX; } info->data.max_charger_voltage_setting = info->data.max_charger_voltage; if (of_property_read_u32(np, "min_charger_voltage", &val) >= 0) info->data.min_charger_voltage = val; else { chr_err("use default V_CHARGER_MIN:%d\n", V_CHARGER_MIN); info->data.min_charger_voltage = V_CHARGER_MIN; } /* dynamic mivr */ if (of_property_read_u32(np, "min_charger_voltage_1", &val) >= 0) info->data.min_charger_voltage_1 = val; else { chr_err("use default V_CHARGER_MIN_1:%d\n", V_CHARGER_MIN_1); info->data.min_charger_voltage_1 = V_CHARGER_MIN_1; } if (of_property_read_u32(np, "min_charger_voltage_2", &val) >= 0) info->data.min_charger_voltage_2 = val; else { chr_err("use default V_CHARGER_MIN_2:%d\n", V_CHARGER_MIN_2); info->data.min_charger_voltage_2 = V_CHARGER_MIN_2; } if (of_property_read_u32(np, "max_dmivr_charger_current", &val) >= 0) info->data.max_dmivr_charger_current = val; else { chr_err("use default MAX_DMIVR_CHARGER_CURRENT:%d\n", MAX_DMIVR_CHARGER_CURRENT); info->data.max_dmivr_charger_current = MAX_DMIVR_CHARGER_CURRENT; } /* charging current */ if (of_property_read_u32(np, "usb_charger_current_suspend", &val) >= 0) info->data.usb_charger_current_suspend = val; else { chr_err("use default USB_CHARGER_CURRENT_SUSPEND:%d\n", USB_CHARGER_CURRENT_SUSPEND); info->data.usb_charger_current_suspend = USB_CHARGER_CURRENT_SUSPEND; } if (of_property_read_u32(np, "usb_charger_current_unconfigured", &val) >= 0) { info->data.usb_charger_current_unconfigured = val; } else { chr_err("use default USB_CHARGER_CURRENT_UNCONFIGURED:%d\n", USB_CHARGER_CURRENT_UNCONFIGURED); info->data.usb_charger_current_unconfigured = USB_CHARGER_CURRENT_UNCONFIGURED; } if (of_property_read_u32(np, "usb_charger_current_configured", &val) >= 0) { info->data.usb_charger_current_configured = val; } else { chr_err("use default USB_CHARGER_CURRENT_CONFIGURED:%d\n", USB_CHARGER_CURRENT_CONFIGURED); info->data.usb_charger_current_configured = USB_CHARGER_CURRENT_CONFIGURED; } if (of_property_read_u32(np, "usb_charger_current", &val) >= 0) { info->data.usb_charger_current = val; } else { chr_err("use default USB_CHARGER_CURRENT:%d\n", USB_CHARGER_CURRENT); info->data.usb_charger_current = USB_CHARGER_CURRENT; } if (of_property_read_u32(np, "ac_charger_current", &val) >= 0) { info->data.ac_charger_current = val; } else { chr_err("use default AC_CHARGER_CURRENT:%d\n", AC_CHARGER_CURRENT); info->data.ac_charger_current = AC_CHARGER_CURRENT; } info->data.pd_charger_current = 3000000; if (of_property_read_u32(np, "ac_charger_input_current", &val) >= 0) info->data.ac_charger_input_current = val; else { chr_err("use default AC_CHARGER_INPUT_CURRENT:%d\n", AC_CHARGER_INPUT_CURRENT); info->data.ac_charger_input_current = AC_CHARGER_INPUT_CURRENT; } if (of_property_read_u32(np, "non_std_ac_charger_current", &val) >= 0) info->data.non_std_ac_charger_current = val; else { chr_err("use default NON_STD_AC_CHARGER_CURRENT:%d\n", NON_STD_AC_CHARGER_CURRENT); info->data.non_std_ac_charger_current = NON_STD_AC_CHARGER_CURRENT; } if (of_property_read_u32(np, "charging_host_charger_current", &val) >= 0) { info->data.charging_host_charger_current = val; } else { chr_err("use default CHARGING_HOST_CHARGER_CURRENT:%d\n", CHARGING_HOST_CHARGER_CURRENT); info->data.charging_host_charger_current = CHARGING_HOST_CHARGER_CURRENT; } if (of_property_read_u32(np, "apple_1_0a_charger_current", &val) >= 0) info->data.apple_1_0a_charger_current = val; else { chr_err("use default APPLE_1_0A_CHARGER_CURRENT:%d\n", APPLE_1_0A_CHARGER_CURRENT); info->data.apple_1_0a_charger_current = APPLE_1_0A_CHARGER_CURRENT; } if (of_property_read_u32(np, "apple_2_1a_charger_current", &val) >= 0) info->data.apple_2_1a_charger_current = val; else { chr_err("use default APPLE_2_1A_CHARGER_CURRENT:%d\n", APPLE_2_1A_CHARGER_CURRENT); info->data.apple_2_1a_charger_current = APPLE_2_1A_CHARGER_CURRENT; } if (of_property_read_u32(np, "ta_ac_charger_current", &val) >= 0) info->data.ta_ac_charger_current = val; else { chr_err("use default TA_AC_CHARGING_CURRENT:%d\n", TA_AC_CHARGING_CURRENT); info->data.ta_ac_charger_current = TA_AC_CHARGING_CURRENT; } if (of_property_read_u32(np, "usb_unlimited_current", &val) >= 0) info->data.usb_unlimited_current = val; else { chr_err("use default usb_unlimited_current:%d\n", USB_UNLIMITED_CURRENT); info->data.usb_unlimited_current = USB_UNLIMITED_CURRENT; } /* sw jeita */ if (of_property_read_u32(np, "jeita_temp_above_t4_cv", &val) >= 0) info->data.jeita_temp_above_t4_cv = val; else { chr_err("use default JEITA_TEMP_ABOVE_T4_CV:%d\n", JEITA_TEMP_ABOVE_T4_CV); info->data.jeita_temp_above_t4_cv = JEITA_TEMP_ABOVE_T4_CV; } if (of_property_read_u32(np, "jeita_temp_t3_to_t4_cv", &val) >= 0) info->data.jeita_temp_t3_to_t4_cv = val; else { chr_err("use default JEITA_TEMP_T3_TO_T4_CV:%d\n", JEITA_TEMP_T3_TO_T4_CV); info->data.jeita_temp_t3_to_t4_cv = JEITA_TEMP_T3_TO_T4_CV; } if (of_property_read_u32(np, "jeita_temp_t2_to_t3_cv", &val) >= 0) info->data.jeita_temp_t2_to_t3_cv = val; else { chr_err("use default JEITA_TEMP_T2_TO_T3_CV:%d\n", JEITA_TEMP_T2_TO_T3_CV); info->data.jeita_temp_t2_to_t3_cv = JEITA_TEMP_T2_TO_T3_CV; } if (of_property_read_u32(np, "jeita_temp_t1_to_t2_cv", &val) >= 0) info->data.jeita_temp_t1_to_t2_cv = val; else { chr_err("use default JEITA_TEMP_T1_TO_T2_CV:%d\n", JEITA_TEMP_T1_TO_T2_CV); info->data.jeita_temp_t1_to_t2_cv = JEITA_TEMP_T1_TO_T2_CV; } if (of_property_read_u32(np, "jeita_temp_t0_to_t1_cv", &val) >= 0) info->data.jeita_temp_t0_to_t1_cv = val; else { chr_err("use default JEITA_TEMP_T0_TO_T1_CV:%d\n", JEITA_TEMP_T0_TO_T1_CV); info->data.jeita_temp_t0_to_t1_cv = JEITA_TEMP_T0_TO_T1_CV; } if (of_property_read_u32(np, "jeita_temp_below_t0_cv", &val) >= 0) info->data.jeita_temp_below_t0_cv = val; else { chr_err("use default JEITA_TEMP_BELOW_T0_CV:%d\n", JEITA_TEMP_BELOW_T0_CV); info->data.jeita_temp_below_t0_cv = JEITA_TEMP_BELOW_T0_CV; } if (of_property_read_u32(np, "temp_t4_thres", &val) >= 0) info->data.temp_t4_thres = val; else { chr_err("use default TEMP_T4_THRES:%d\n", TEMP_T4_THRES); info->data.temp_t4_thres = TEMP_T4_THRES; } if (of_property_read_u32(np, "temp_t4_thres_minus_x_degree", &val) >= 0) info->data.temp_t4_thres_minus_x_degree = val; else { chr_err("use default TEMP_T4_THRES_MINUS_X_DEGREE:%d\n", TEMP_T4_THRES_MINUS_X_DEGREE); info->data.temp_t4_thres_minus_x_degree = TEMP_T4_THRES_MINUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t3_thres", &val) >= 0) info->data.temp_t3_thres = val; else { chr_err("use default TEMP_T3_THRES:%d\n", TEMP_T3_THRES); info->data.temp_t3_thres = TEMP_T3_THRES; } if (of_property_read_u32(np, "temp_t3_thres_minus_x_degree", &val) >= 0) info->data.temp_t3_thres_minus_x_degree = val; else { chr_err("use default TEMP_T3_THRES_MINUS_X_DEGREE:%d\n", TEMP_T3_THRES_MINUS_X_DEGREE); info->data.temp_t3_thres_minus_x_degree = TEMP_T3_THRES_MINUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t2_thres", &val) >= 0) info->data.temp_t2_thres = val; else { chr_err("use default TEMP_T2_THRES:%d\n", TEMP_T2_THRES); info->data.temp_t2_thres = TEMP_T2_THRES; } if (of_property_read_u32(np, "temp_t2_thres_plus_x_degree", &val) >= 0) info->data.temp_t2_thres_plus_x_degree = val; else { chr_err("use default TEMP_T2_THRES_PLUS_X_DEGREE:%d\n", TEMP_T2_THRES_PLUS_X_DEGREE); info->data.temp_t2_thres_plus_x_degree = TEMP_T2_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t1_thres", &val) >= 0) info->data.temp_t1_thres = val; else { chr_err("use default TEMP_T1_THRES:%d\n", TEMP_T1_THRES); info->data.temp_t1_thres = TEMP_T1_THRES; } if (of_property_read_u32(np, "temp_t1_thres_plus_x_degree", &val) >= 0) info->data.temp_t1_thres_plus_x_degree = val; else { chr_err("use default TEMP_T1_THRES_PLUS_X_DEGREE:%d\n", TEMP_T1_THRES_PLUS_X_DEGREE); info->data.temp_t1_thres_plus_x_degree = TEMP_T1_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t0_thres", &val) >= 0) info->data.temp_t0_thres = val; else { chr_err("use default TEMP_T0_THRES:%d\n", TEMP_T0_THRES); info->data.temp_t0_thres = TEMP_T0_THRES; } if (of_property_read_u32(np, "temp_t0_thres_plus_x_degree", &val) >= 0) info->data.temp_t0_thres_plus_x_degree = val; else { chr_err("use default TEMP_T0_THRES_PLUS_X_DEGREE:%d\n", TEMP_T0_THRES_PLUS_X_DEGREE); info->data.temp_t0_thres_plus_x_degree = TEMP_T0_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_neg_10_thres", &val) >= 0) info->data.temp_neg_10_thres = val; else { chr_err("use default TEMP_NEG_10_THRES:%d\n", TEMP_NEG_10_THRES); info->data.temp_neg_10_thres = TEMP_NEG_10_THRES; } /* battery temperature protection */ info->thermal.sm = BAT_TEMP_NORMAL; info->thermal.enable_min_charge_temp = of_property_read_bool(np, "enable_min_charge_temp"); if (of_property_read_u32(np, "min_charge_temp", &val) >= 0) info->thermal.min_charge_temp = val; else { chr_err("use default MIN_CHARGE_TEMP:%d\n", MIN_CHARGE_TEMP); info->thermal.min_charge_temp = MIN_CHARGE_TEMP; } if (of_property_read_u32(np, "min_charge_temp_plus_x_degree", &val) >= 0) { info->thermal.min_charge_temp_plus_x_degree = val; } else { chr_err("use default MIN_CHARGE_TEMP_PLUS_X_DEGREE:%d\n", MIN_CHARGE_TEMP_PLUS_X_DEGREE); info->thermal.min_charge_temp_plus_x_degree = MIN_CHARGE_TEMP_PLUS_X_DEGREE; } if (of_property_read_u32(np, "max_charge_temp", &val) >= 0) info->thermal.max_charge_temp = val; else { chr_err("use default MAX_CHARGE_TEMP:%d\n", MAX_CHARGE_TEMP); info->thermal.max_charge_temp = MAX_CHARGE_TEMP; } if (of_property_read_u32(np, "max_charge_temp_minus_x_degree", &val) >= 0) { info->thermal.max_charge_temp_minus_x_degree = val; } else { chr_err("use default MAX_CHARGE_TEMP_MINUS_X_DEGREE:%d\n", MAX_CHARGE_TEMP_MINUS_X_DEGREE); info->thermal.max_charge_temp_minus_x_degree = MAX_CHARGE_TEMP_MINUS_X_DEGREE; } /* PE */ info->data.ta_12v_support = of_property_read_bool(np, "ta_12v_support"); info->data.ta_9v_support = of_property_read_bool(np, "ta_9v_support"); if (of_property_read_u32(np, "pe_ichg_level_threshold", &val) >= 0) info->data.pe_ichg_level_threshold = val; else { chr_err("use default PE_ICHG_LEAVE_THRESHOLD:%d\n", PE_ICHG_LEAVE_THRESHOLD); info->data.pe_ichg_level_threshold = PE_ICHG_LEAVE_THRESHOLD; } if (of_property_read_u32(np, "ta_ac_12v_input_current", &val) >= 0) info->data.ta_ac_12v_input_current = val; else { chr_err("use default TA_AC_12V_INPUT_CURRENT:%d\n", TA_AC_12V_INPUT_CURRENT); info->data.ta_ac_12v_input_current = TA_AC_12V_INPUT_CURRENT; } if (of_property_read_u32(np, "ta_ac_9v_input_current", &val) >= 0) info->data.ta_ac_9v_input_current = val; else { chr_err("use default TA_AC_9V_INPUT_CURRENT:%d\n", TA_AC_9V_INPUT_CURRENT); info->data.ta_ac_9v_input_current = TA_AC_9V_INPUT_CURRENT; } if (of_property_read_u32(np, "ta_ac_7v_input_current", &val) >= 0) info->data.ta_ac_7v_input_current = val; else { chr_err("use default TA_AC_7V_INPUT_CURRENT:%d\n", TA_AC_7V_INPUT_CURRENT); info->data.ta_ac_7v_input_current = TA_AC_7V_INPUT_CURRENT; } /* PE 2.0 */ if (of_property_read_u32(np, "pe20_ichg_level_threshold", &val) >= 0) info->data.pe20_ichg_level_threshold = val; else { chr_err("use default PE20_ICHG_LEAVE_THRESHOLD:%d\n", PE20_ICHG_LEAVE_THRESHOLD); info->data.pe20_ichg_level_threshold = PE20_ICHG_LEAVE_THRESHOLD; } if (of_property_read_u32(np, "ta_start_battery_soc", &val) >= 0) info->data.ta_start_battery_soc = val; else { chr_err("use default TA_START_BATTERY_SOC:%d\n", TA_START_BATTERY_SOC); info->data.ta_start_battery_soc = TA_START_BATTERY_SOC; } if (of_property_read_u32(np, "ta_stop_battery_soc", &val) >= 0) info->data.ta_stop_battery_soc = val; else { chr_err("use default TA_STOP_BATTERY_SOC:%d\n", TA_STOP_BATTERY_SOC); info->data.ta_stop_battery_soc = TA_STOP_BATTERY_SOC; } /* PE 4.0 */ if (of_property_read_u32(np, "high_temp_to_leave_pe40", &val) >= 0) { info->data.high_temp_to_leave_pe40 = val; } else { chr_err("use default high_temp_to_leave_pe40:%d\n", HIGH_TEMP_TO_LEAVE_PE40); info->data.high_temp_to_leave_pe40 = HIGH_TEMP_TO_LEAVE_PE40; } if (of_property_read_u32(np, "high_temp_to_enter_pe40", &val) >= 0) { info->data.high_temp_to_enter_pe40 = val; } else { chr_err("use default high_temp_to_enter_pe40:%d\n", HIGH_TEMP_TO_ENTER_PE40); info->data.high_temp_to_enter_pe40 = HIGH_TEMP_TO_ENTER_PE40; } if (of_property_read_u32(np, "low_temp_to_leave_pe40", &val) >= 0) { info->data.low_temp_to_leave_pe40 = val; } else { chr_err("use default low_temp_to_leave_pe40:%d\n", LOW_TEMP_TO_LEAVE_PE40); info->data.low_temp_to_leave_pe40 = LOW_TEMP_TO_LEAVE_PE40; } if (of_property_read_u32(np, "low_temp_to_enter_pe40", &val) >= 0) { info->data.low_temp_to_enter_pe40 = val; } else { chr_err("use default low_temp_to_enter_pe40:%d\n", LOW_TEMP_TO_ENTER_PE40); info->data.low_temp_to_enter_pe40 = LOW_TEMP_TO_ENTER_PE40; } /* PE 4.0 single */ if (of_property_read_u32(np, "pe40_single_charger_input_current", &val) >= 0) { info->data.pe40_single_charger_input_current = val; } else { chr_err("use default pe40_single_charger_input_current:%d\n", 3000); info->data.pe40_single_charger_input_current = 3000; } if (of_property_read_u32(np, "pe40_single_charger_current", &val) >= 0) { info->data.pe40_single_charger_current = val; } else { chr_err("use default pe40_single_charger_current:%d\n", 3000); info->data.pe40_single_charger_current = 3000; } /* PE 4.0 dual */ if (of_property_read_u32(np, "pe40_dual_charger_input_current", &val) >= 0) { info->data.pe40_dual_charger_input_current = val; } else { chr_err("use default pe40_dual_charger_input_current:%d\n", 3000); info->data.pe40_dual_charger_input_current = 3000; } if (of_property_read_u32(np, "pe40_dual_charger_chg1_current", &val) >= 0) { info->data.pe40_dual_charger_chg1_current = val; } else { chr_err("use default pe40_dual_charger_chg1_current:%d\n", 2000); info->data.pe40_dual_charger_chg1_current = 2000; } if (of_property_read_u32(np, "pe40_dual_charger_chg2_current", &val) >= 0) { info->data.pe40_dual_charger_chg2_current = val; } else { chr_err("use default pe40_dual_charger_chg2_current:%d\n", 2000); info->data.pe40_dual_charger_chg2_current = 2000; } if (of_property_read_u32(np, "dual_polling_ieoc", &val) >= 0) info->data.dual_polling_ieoc = val; else { chr_err("use default dual_polling_ieoc :%d\n", 750000); info->data.dual_polling_ieoc = 750000; } if (of_property_read_u32(np, "pe40_stop_battery_soc", &val) >= 0) info->data.pe40_stop_battery_soc = val; else { chr_err("use default pe40_stop_battery_soc:%d\n", 80); info->data.pe40_stop_battery_soc = 80; } if (of_property_read_u32(np, "pe40_max_vbus", &val) >= 0) info->data.pe40_max_vbus = val; else { chr_err("use default pe40_max_vbus:%d\n", PE40_MAX_VBUS); info->data.pe40_max_vbus = PE40_MAX_VBUS; } if (of_property_read_u32(np, "pe40_max_ibus", &val) >= 0) info->data.pe40_max_ibus = val; else { chr_err("use default pe40_max_ibus:%d\n", PE40_MAX_IBUS); info->data.pe40_max_ibus = PE40_MAX_IBUS; } /* PE 4.0 cable impedance (mohm) */ if (of_property_read_u32(np, "pe40_r_cable_1a_lower", &val) >= 0) info->data.pe40_r_cable_1a_lower = val; else { chr_err("use default pe40_r_cable_1a_lower:%d\n", 530); info->data.pe40_r_cable_1a_lower = 530; } if (of_property_read_u32(np, "pe40_r_cable_2a_lower", &val) >= 0) info->data.pe40_r_cable_2a_lower = val; else { chr_err("use default pe40_r_cable_2a_lower:%d\n", 390); info->data.pe40_r_cable_2a_lower = 390; } if (of_property_read_u32(np, "pe40_r_cable_3a_lower", &val) >= 0) info->data.pe40_r_cable_3a_lower = val; else { chr_err("use default pe40_r_cable_3a_lower:%d\n", 252); info->data.pe40_r_cable_3a_lower = 252; } /* PD */ if (of_property_read_u32(np, "pd_vbus_upper_bound", &val) >= 0) { info->data.pd_vbus_upper_bound = val; } else { chr_err("use default pd_vbus_upper_bound:%d\n", PD_VBUS_UPPER_BOUND); info->data.pd_vbus_upper_bound = PD_VBUS_UPPER_BOUND; } if (of_property_read_u32(np, "pd_vbus_low_bound", &val) >= 0) { info->data.pd_vbus_low_bound = val; } else { chr_err("use default pd_vbus_low_bound:%d\n", PD_VBUS_LOW_BOUND); info->data.pd_vbus_low_bound = PD_VBUS_LOW_BOUND; } if (of_property_read_u32(np, "pd_ichg_level_threshold", &val) >= 0) info->data.pd_ichg_level_threshold = val; else { chr_err("use default pd_ichg_level_threshold:%d\n", PD_ICHG_LEAVE_THRESHOLD); info->data.pd_ichg_level_threshold = PD_ICHG_LEAVE_THRESHOLD; } if (of_property_read_u32(np, "pd_stop_battery_soc", &val) >= 0) info->data.pd_stop_battery_soc = val; else { chr_err("use default pd_stop_battery_soc:%d\n", PD_STOP_BATTERY_SOC); info->data.pd_stop_battery_soc = PD_STOP_BATTERY_SOC; } if (of_property_read_u32(np, "vsys_watt", &val) >= 0) { info->data.vsys_watt = val; } else { chr_err("use default vsys_watt:%d\n", VSYS_WATT); info->data.vsys_watt = VSYS_WATT; } if (of_property_read_u32(np, "ibus_err", &val) >= 0) { info->data.ibus_err = val; } else { chr_err("use default ibus_err:%d\n", IBUS_ERR); info->data.ibus_err = IBUS_ERR; } /* dual charger */ if (of_property_read_u32(np, "chg1_ta_ac_charger_current", &val) >= 0) info->data.chg1_ta_ac_charger_current = val; else { chr_err("use default TA_AC_MASTER_CHARGING_CURRENT:%d\n", TA_AC_MASTER_CHARGING_CURRENT); info->data.chg1_ta_ac_charger_current = TA_AC_MASTER_CHARGING_CURRENT; } if (of_property_read_u32(np, "chg2_ta_ac_charger_current", &val) >= 0) info->data.chg2_ta_ac_charger_current = val; else { chr_err("use default TA_AC_SLAVE_CHARGING_CURRENT:%d\n", TA_AC_SLAVE_CHARGING_CURRENT); info->data.chg2_ta_ac_charger_current = TA_AC_SLAVE_CHARGING_CURRENT; } if (of_property_read_u32(np, "slave_mivr_diff", &val) >= 0) info->data.slave_mivr_diff = val; else { chr_err("use default SLAVE_MIVR_DIFF:%d\n", SLAVE_MIVR_DIFF); info->data.slave_mivr_diff = SLAVE_MIVR_DIFF; } /* slave charger */ if (of_property_read_u32(np, "chg2_eff", &val) >= 0) info->data.chg2_eff = val; else { chr_err("use default CHG2_EFF:%d\n", CHG2_EFF); info->data.chg2_eff = CHG2_EFF; } info->data.parallel_vbus = of_property_read_bool(np, "parallel_vbus"); /* cable measurement impedance */ if (of_property_read_u32(np, "cable_imp_threshold", &val) >= 0) info->data.cable_imp_threshold = val; else { chr_err("use default CABLE_IMP_THRESHOLD:%d\n", CABLE_IMP_THRESHOLD); info->data.cable_imp_threshold = CABLE_IMP_THRESHOLD; } if (of_property_read_u32(np, "vbat_cable_imp_threshold", &val) >= 0) info->data.vbat_cable_imp_threshold = val; else { chr_err("use default VBAT_CABLE_IMP_THRESHOLD:%d\n", VBAT_CABLE_IMP_THRESHOLD); info->data.vbat_cable_imp_threshold = VBAT_CABLE_IMP_THRESHOLD; } /* BIF */ if (of_property_read_u32(np, "bif_threshold1", &val) >= 0) info->data.bif_threshold1 = val; else { chr_err("use default BIF_THRESHOLD1:%d\n", BIF_THRESHOLD1); info->data.bif_threshold1 = BIF_THRESHOLD1; } if (of_property_read_u32(np, "bif_threshold2", &val) >= 0) info->data.bif_threshold2 = val; else { chr_err("use default BIF_THRESHOLD2:%d\n", BIF_THRESHOLD2); info->data.bif_threshold2 = BIF_THRESHOLD2; } if (of_property_read_u32(np, "bif_cv_under_threshold2", &val) >= 0) info->data.bif_cv_under_threshold2 = val; else { chr_err("use default BIF_CV_UNDER_THRESHOLD2:%d\n", BIF_CV_UNDER_THRESHOLD2); info->data.bif_cv_under_threshold2 = BIF_CV_UNDER_THRESHOLD2; } info->data.power_path_support = of_property_read_bool(np, "power_path_support"); chr_debug("%s: power_path_support: %d\n", __func__, info->data.power_path_support); if (of_property_read_u32(np, "max_charging_time", &val) >= 0) info->data.max_charging_time = val; else { chr_err("use default MAX_CHARGING_TIME:%d\n", MAX_CHARGING_TIME); info->data.max_charging_time = MAX_CHARGING_TIME; } if (of_property_read_u32(np, "bc12_charger", &val) >= 0) info->data.bc12_charger = val; else { chr_err("use default BC12_CHARGER:%d\n", DEFAULT_BC12_CHARGER); info->data.bc12_charger = DEFAULT_BC12_CHARGER; } if (strcmp(info->algorithm_name, "SwitchCharging2") == 0) { chr_err("found SwitchCharging2\n"); mtk_switch_charging_init2(info); } if (of_property_read_u32(np, "sc_battery_size", &val) >= 0) info->sc.battery_size = val; else { chr_err("use default sc_battery_size:%d\n", SC_BATTERY_SIZE); info->sc.battery_size = SC_BATTERY_SIZE; } if (of_property_read_u32(np, "sc_cv_time", &val) >= 0) info->sc.left_time_for_cv = val; else { chr_err("use default sc_cv_time:%d\n", SC_CV_TIME); info->sc.left_time_for_cv = SC_CV_TIME; } if (of_property_read_u32(np, "sc_current_limit", &val) >= 0) info->sc.current_limit = val; else { chr_err("use default sc_current_limit:%d\n", SC_CURRENT_LIMIT); info->sc.current_limit = SC_CURRENT_LIMIT; } chr_err("algorithm name:%s\n", info->algorithm_name); return 0; } static ssize_t show_Pump_Express(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; int is_ta_detected = 0; pr_debug("[%s] chr_type:%d UISOC:%d startsoc:%d stopsoc:%d\n", __func__, mt_get_charger_type(), battery_get_uisoc(), pinfo->data.ta_start_battery_soc, pinfo->data.ta_stop_battery_soc); if (IS_ENABLED(CONFIG_MTK_PUMP_EXPRESS_PLUS_20_SUPPORT)) { /* Is PE+20 connect */ if (mtk_pe20_get_is_connect(pinfo)) is_ta_detected = 1; } if (IS_ENABLED(CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT)) { /* Is PE+ connect */ if (mtk_pe_get_is_connect(pinfo)) is_ta_detected = 1; } if (mtk_is_TA_support_pd_pps(pinfo) == true || pinfo->is_pdc_run == true) is_ta_detected = 1; pr_debug("%s: detected = %d, pe20_connect = %d, pe_connect = %d\n", __func__, is_ta_detected, mtk_pe20_get_is_connect(pinfo), mtk_pe_get_is_connect(pinfo)); return sprintf(buf, "%u\n", is_ta_detected); } static DEVICE_ATTR(Pump_Express, 0444, show_Pump_Express, NULL); static ssize_t show_input_current(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; pr_debug("[Battery] %s : %x\n", __func__, pinfo->chg1_data.thermal_input_current_limit); return sprintf(buf, "%u\n", pinfo->chg1_data.thermal_input_current_limit); } static ssize_t store_input_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; unsigned int reg = 0; int ret; pr_debug("[Battery] %s\n", __func__); if (buf != NULL && size != 0) { pr_debug("[Battery] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); pinfo->chg1_data.thermal_input_current_limit = reg; if (pinfo->data.parallel_vbus) pinfo->chg2_data.thermal_input_current_limit = reg; pr_debug("[Battery] %s: %x\n", __func__, pinfo->chg1_data.thermal_input_current_limit); } return size; } static DEVICE_ATTR(input_current, 0644, show_input_current, store_input_current); static ssize_t show_chg1_current(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; pr_debug("[Battery] %s : %x\n", __func__, pinfo->chg1_data.thermal_charging_current_limit); return sprintf(buf, "%u\n", pinfo->chg1_data.thermal_charging_current_limit); } static ssize_t store_chg1_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; unsigned int reg = 0; int ret; pr_debug("[Battery] %s\n", __func__); if (buf != NULL && size != 0) { pr_debug("[Battery] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); pinfo->chg1_data.thermal_charging_current_limit = reg; pr_debug("[Battery] %s: %x\n", __func__, pinfo->chg1_data.thermal_charging_current_limit); } return size; } static DEVICE_ATTR(chg1_current, 0644, show_chg1_current, store_chg1_current); static ssize_t show_chg2_current(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; pr_debug("[Battery] %s : %x\n", __func__, pinfo->chg2_data.thermal_charging_current_limit); return sprintf(buf, "%u\n", pinfo->chg2_data.thermal_charging_current_limit); } static ssize_t store_chg2_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; unsigned int reg = 0; int ret; pr_debug("[Battery] %s\n", __func__); if (buf != NULL && size != 0) { pr_debug("[Battery] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); pinfo->chg2_data.thermal_charging_current_limit = reg; pr_debug("[Battery] %s: %x\n", __func__, pinfo->chg2_data.thermal_charging_current_limit); } return size; } static DEVICE_ATTR(chg2_current, 0644, show_chg2_current, store_chg2_current); static ssize_t show_BatNotify(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; pr_debug("[Battery] show_BatteryNotify: 0x%x\n", pinfo->notify_code); return sprintf(buf, "%u\n", pinfo->notify_code); } static ssize_t store_BatNotify(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; unsigned int reg = 0; int ret; pr_debug("[Battery] store_BatteryNotify\n"); if (buf != NULL && size != 0) { pr_debug("[Battery] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); pinfo->notify_code = reg; pr_debug("[Battery] store code: 0x%x\n", pinfo->notify_code); mtk_chgstat_notify(pinfo); } return size; } static DEVICE_ATTR(BatteryNotify, 0644, show_BatNotify, store_BatNotify); static ssize_t show_BN_TestMode(struct device *dev, struct device_attribute *attr, char *buf) { struct charger_manager *pinfo = dev->driver_data; pr_debug("[Battery] %s : %x\n", __func__, pinfo->notify_test_mode); return sprintf(buf, "%u\n", pinfo->notify_test_mode); } static ssize_t store_BN_TestMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct charger_manager *pinfo = dev->driver_data; unsigned int reg = 0; int ret; pr_debug("[Battery] %s\n", __func__); if (buf != NULL && size != 0) { pr_debug("[Battery] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); pinfo->notify_test_mode = reg; pr_debug("[Battery] store mode: %x\n", pinfo->notify_test_mode); } return size; } static DEVICE_ATTR(BN_TestMode, 0644, show_BN_TestMode, store_BN_TestMode); static ssize_t show_ADC_Charger_Voltage(struct device *dev, struct device_attribute *attr, char *buf) { int vbus = battery_get_vbus(); if (!atomic_read(&pinfo->enable_kpoc_shdn) || vbus < 0) { chr_err("HardReset or get vbus failed, vbus:%d:5000\n", vbus); vbus = 5000; } pr_debug("[%s]: %d\n", __func__, vbus); return sprintf(buf, "%d\n", vbus); } static DEVICE_ATTR(ADC_Charger_Voltage, 0444, show_ADC_Charger_Voltage, NULL); /* procfs */ static int mtk_chg_current_cmd_show(struct seq_file *m, void *data) { struct charger_manager *pinfo = m->private; seq_printf(m, "%d %d\n", pinfo->usb_unlimited, pinfo->cmd_discharging); return 0; } static ssize_t mtk_chg_current_cmd_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0; char desc[32] = {0}; int current_unlimited = 0; int cmd_discharging = 0; struct charger_manager *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; if (sscanf(desc, "%d %d", ¤t_unlimited, &cmd_discharging) == 2) { info->usb_unlimited = current_unlimited; if (cmd_discharging == 1) { info->cmd_discharging = true; charger_dev_enable(info->chg1_dev, false); charger_manager_notifier(info, CHARGER_NOTIFY_STOP_CHARGING); } else if (cmd_discharging == 0) { info->cmd_discharging = false; charger_dev_enable(info->chg1_dev, true); charger_manager_notifier(info, CHARGER_NOTIFY_START_CHARGING); } pr_debug("%s current_unlimited=%d, cmd_discharging=%d\n", __func__, current_unlimited, cmd_discharging); return count; } chr_err("bad argument, echo [usb_unlimited] [disable] > current_cmd\n"); return count; } static int mtk_chg_en_power_path_show(struct seq_file *m, void *data) { struct charger_manager *pinfo = m->private; bool power_path_en = true; charger_dev_is_powerpath_enabled(pinfo->chg1_dev, &power_path_en); seq_printf(m, "%d\n", power_path_en); return 0; } static ssize_t mtk_chg_en_power_path_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int enable = 0; struct charger_manager *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &enable); if (ret == 0) { #if !defined(CONFIG_BATTERY_SAMSUNG) charger_dev_enable_powerpath(info->chg1_dev, enable); #endif pr_debug("%s: enable power path = %d\n", __func__, enable); return count; } chr_err("bad argument, echo [enable] > en_power_path\n"); return count; } static int mtk_chg_en_safety_timer_show(struct seq_file *m, void *data) { struct charger_manager *pinfo = m->private; bool safety_timer_en = false; charger_dev_is_safety_timer_enabled(pinfo->chg1_dev, &safety_timer_en); seq_printf(m, "%d\n", safety_timer_en); return 0; } static ssize_t mtk_chg_en_safety_timer_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int enable = 0; struct charger_manager *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &enable); if (ret == 0) { charger_dev_enable_safety_timer(info->chg1_dev, enable); pr_debug("%s: enable safety timer = %d\n", __func__, enable); /* SW safety timer */ if (info->sw_safety_timer_setting == true) { if (enable) info->enable_sw_safety_timer = true; else info->enable_sw_safety_timer = false; } return count; } chr_err("bad argument, echo [enable] > en_safety_timer\n"); return count; } static int mtk_chg_set_cv_show(struct seq_file *m, void *data) { struct charger_manager *pinfo = m->private; seq_printf(m, "%d\n", pinfo->data.battery_cv); return 0; } static ssize_t mtk_chg_set_cv_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int cv = 0; struct charger_manager *info = PDE_DATA(file_inode(file)); struct power_supply *psy = NULL; union power_supply_propval dynamic_cv; if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &cv); if (ret == 0) { if (cv >= CV_HIGH_THRESHOLD) { info->data.battery_cv = BATTERY_CV; chr_info("%s: adjust charge voltage %dV too high, use default cv\n", __func__, cv); } else { info->data.battery_cv = cv; chr_info("%s: adjust charge voltage = %dV\n", __func__, cv); } psy = power_supply_get_by_name("battery"); if (!IS_ERR_OR_NULL(psy)) { dynamic_cv.intval = info->data.battery_cv; ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, &dynamic_cv); if (ret < 0) chr_err("set gauge cv fail\n"); } return count; } chr_err("%s: bad argument\n", __func__); return count; } /* PROC_FOPS_RW(battery_cmd); */ /* PROC_FOPS_RW(discharging_cmd); */ PROC_FOPS_RW(current_cmd); PROC_FOPS_RW(en_power_path); PROC_FOPS_RW(en_safety_timer); PROC_FOPS_RW(set_cv); /* Create sysfs and procfs attributes */ static int mtk_charger_setup_files(struct platform_device *pdev) { int ret = 0; struct proc_dir_entry *battery_dir = NULL; struct charger_manager *info = platform_get_drvdata(pdev); /* struct charger_device *chg_dev; */ ret = device_create_file(&(pdev->dev), &dev_attr_sw_jeita); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_pe20); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_pe40); if (ret) goto _out; /* Battery warning */ ret = device_create_file(&(pdev->dev), &dev_attr_BatteryNotify); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_BN_TestMode); if (ret) goto _out; /* Pump express */ ret = device_create_file(&(pdev->dev), &dev_attr_Pump_Express); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_charger_log_level); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_pdc_max_watt); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_ADC_Charger_Voltage); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_input_current); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_chg1_current); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_chg2_current); if (ret) goto _out; battery_dir = proc_mkdir("mtk_battery_cmd", NULL); if (!battery_dir) { chr_err("[%s]: mkdir /proc/mtk_battery_cmd failed\n", __func__); return -ENOMEM; } proc_create_data("current_cmd", 0640, battery_dir, &mtk_chg_current_cmd_fops, info); proc_create_data("en_power_path", 0640, battery_dir, &mtk_chg_en_power_path_fops, info); proc_create_data("en_safety_timer", 0640, battery_dir, &mtk_chg_en_safety_timer_fops, info); proc_create_data("set_cv", 0640, battery_dir, &mtk_chg_set_cv_fops, info); _out: return ret; } void notify_adapter_event(enum adapter_type type, enum adapter_event evt, void *val) { chr_err("%s %d %d\n", __func__, type, evt); switch (type) { case MTK_PD_ADAPTER: switch (evt) { case MTK_PD_CONNECT_NONE: #if defined(CONFIG_BATTERY_SAMSUNG) && defined(CONFIG_PDIC_NOTIFIER) pdc_clear(); #endif mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify Detach\n"); pinfo->pd_type = MTK_PD_CONNECT_NONE; mutex_unlock(&pinfo->charger_pd_lock); /* reset PE40 */ break; case MTK_PD_CONNECT_HARD_RESET: #if defined(CONFIG_BATTERY_SAMSUNG) && defined(CONFIG_PDIC_NOTIFIER) pdc_hard_rst(); #endif mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify HardReset\n"); pinfo->pd_type = MTK_PD_CONNECT_NONE; pinfo->pd_reset = true; mutex_unlock(&pinfo->charger_pd_lock); _wake_up_charger(pinfo); /* reset PE40 */ break; case MTK_PD_CONNECT_PE_READY_SNK: mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify fixe voltage ready\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK; mutex_unlock(&pinfo->charger_pd_lock); /* PD is ready */ _wake_up_charger(pinfo); break; case MTK_PD_CONNECT_PE_READY_SNK_PD30: mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify PD30 ready\r\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_PD30; mutex_unlock(&pinfo->charger_pd_lock); /* PD30 is ready */ _wake_up_charger(pinfo); break; case MTK_PD_CONNECT_PE_READY_SNK_APDO: mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify APDO Ready\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_APDO; mutex_unlock(&pinfo->charger_pd_lock); /* PE40 is ready */ _wake_up_charger(pinfo); break; case MTK_PD_CONNECT_TYPEC_ONLY_SNK: mutex_lock(&pinfo->charger_pd_lock); chr_err("PD Notify Type-C Ready\n"); pinfo->pd_type = MTK_PD_CONNECT_TYPEC_ONLY_SNK; mutex_unlock(&pinfo->charger_pd_lock); /* type C is ready */ _wake_up_charger(pinfo); break; case MTK_TYPEC_WD_STATUS: chr_err("wd status = %d\n", *(bool *)val); mutex_lock(&pinfo->charger_pd_lock); pinfo->water_detected = *(bool *)val; mutex_unlock(&pinfo->charger_pd_lock); if (pinfo->water_detected == true) { pinfo->notify_code |= CHG_TYPEC_WD_STATUS; charger_dev_enable(pinfo->chg1_dev, false); } else { pinfo->notify_code &= ~CHG_TYPEC_WD_STATUS; charger_dev_enable(pinfo->chg1_dev, true); } mtk_chgstat_notify(pinfo); break; case MTK_TYPEC_HRESET_STATUS: chr_err("hreset status = %d\n", *(bool *)val); mutex_lock(&pinfo->charger_pd_lock); if (*(bool *)val) atomic_set(&pinfo->enable_kpoc_shdn, 1); else atomic_set(&pinfo->enable_kpoc_shdn, 0); mutex_unlock(&pinfo->charger_pd_lock); #ifdef CONFIG_KPOC_GET_SOURCE_CAP_TRY kpoc_power_off_check(pinfo); #endif /*CONFIG_KPOC_GET_SOURCE_CAP_TRY*/ break; }; } mtk_pe50_notifier_call(pinfo, MTK_PE50_NOTISRC_TCP, evt, val); } static int proc_dump_log_show(struct seq_file *m, void *v) { struct adapter_power_cap cap; int i; cap.nr = 0; cap.pdp = 0; for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { cap.max_mv[i] = 0; cap.min_mv[i] = 0; cap.ma[i] = 0; cap.type[i] = 0; cap.pwr_limit[i] = 0; } if (pinfo->pd_type == MTK_PD_CONNECT_PE_READY_SNK_APDO) { seq_puts(m, "********** PD APDO cap Dump **********\n"); adapter_dev_get_cap(pinfo->pd_adapter, MTK_PD_APDO, &cap); for (i = 0; i < cap.nr; i++) { seq_printf(m, "%d: mV:%d,%d mA:%d type:%d pwr_limit:%d pdp:%d\n", i, cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i], cap.pwr_limit[i], cap.pdp); } } else if (pinfo->pd_type == MTK_PD_CONNECT_PE_READY_SNK || pinfo->pd_type == MTK_PD_CONNECT_PE_READY_SNK_PD30) { seq_puts(m, "********** PD cap Dump **********\n"); adapter_dev_get_cap(pinfo->pd_adapter, MTK_PD, &cap); for (i = 0; i < cap.nr; i++) { seq_printf(m, "%d: mV:%d,%d mA:%d type:%d\n", i, cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i]); } } return 0; } static ssize_t proc_write( struct file *file, const char __user *buffer, size_t count, loff_t *f_pos) { return count; } static int proc_dump_log_open(struct inode *inode, struct file *file) { return single_open(file, proc_dump_log_show, NULL); } static const struct file_operations charger_dump_log_proc_fops = { .open = proc_dump_log_open, .read = seq_read, .llseek = seq_lseek, .write = proc_write, }; void charger_debug_init(void) { struct proc_dir_entry *charger_dir; charger_dir = proc_mkdir("charger", NULL); if (!charger_dir) { chr_err("fail to mkdir /proc/charger\n"); return; } proc_create("dump_log", 0640, charger_dir, &charger_dump_log_proc_fops); } void scd_ctrl_cmd_from_user(void *nl_data, struct sc_nl_msg_t *ret_msg) { struct sc_nl_msg_t *msg; msg = nl_data; ret_msg->sc_cmd = msg->sc_cmd; switch (msg->sc_cmd) { case SC_DAEMON_CMD_PRINT_LOG: { chr_err("%s", &msg->sc_data[0]); } break; case SC_DAEMON_CMD_SET_DAEMON_PID: { memcpy(&pinfo->sc.g_scd_pid, &msg->sc_data[0], sizeof(pinfo->sc.g_scd_pid)); chr_err("[fr] SC_DAEMON_CMD_SET_DAEMON_PID = %d(first launch)\n", pinfo->sc.g_scd_pid); } break; case SC_DAEMON_CMD_SETTING: { struct scd_cmd_param_t_1 data; memcpy(&data, &msg->sc_data[0], sizeof(struct scd_cmd_param_t_1)); chr_debug("rcv data:%d %d %d %d %d %d %d %d %d %d %d %d %d %d Ans:%d\n", data.data[0], data.data[1], data.data[2], data.data[3], data.data[4], data.data[5], data.data[6], data.data[7], data.data[8], data.data[9], data.data[10], data.data[11], data.data[12], data.data[13], data.data[14]); pinfo->sc.solution = data.data[SC_SOLUTION]; if (data.data[SC_SOLUTION] == SC_DISABLE) pinfo->sc.disable_charger = true; else if (data.data[SC_SOLUTION] == SC_REDUCE) pinfo->sc.disable_charger = false; else pinfo->sc.disable_charger = false; } break; default: chr_err("bad sc_DAEMON_CTRL_CMD_FROM_USER 0x%x\n", msg->sc_cmd); break; } } static void sc_nl_send_to_user(u32 pid, int seq, struct sc_nl_msg_t *reply_msg) { struct sk_buff *skb; struct nlmsghdr *nlh; /* int size=sizeof(struct fgd_nl_msg_t); */ int size = reply_msg->sc_data_len + SCD_NL_MSG_T_HDR_LEN; int len = NLMSG_SPACE(size); void *data; int ret; reply_msg->identity = SCD_NL_MAGIC; if (in_interrupt()) skb = alloc_skb(len, GFP_ATOMIC); else skb = alloc_skb(len, GFP_KERNEL); if (!skb) return; nlh = nlmsg_put(skb, pid, seq, 0, size, 0); data = NLMSG_DATA(nlh); memcpy(data, reply_msg, size); NETLINK_CB(skb).portid = 0; /* from kernel */ NETLINK_CB(skb).dst_group = 0; /* unicast */ ret = netlink_unicast(pinfo->sc.daemo_nl_sk, skb, pid, MSG_DONTWAIT); if (ret < 0) { chr_err("[Netlink] sc send failed %d\n", ret); return; } } static void chg_nl_data_handler(struct sk_buff *skb) { u32 pid; kuid_t uid; int seq; void *data; struct nlmsghdr *nlh; struct sc_nl_msg_t *sc_msg, *sc_ret_msg; int size = 0; nlh = (struct nlmsghdr *)skb->data; pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); sc_msg = (struct sc_nl_msg_t *)data; size = sc_msg->sc_ret_data_len + SCD_NL_MSG_T_HDR_LEN; if (size > (PAGE_SIZE << 1)) sc_ret_msg = vmalloc(size); else { if (in_interrupt()) sc_ret_msg = kmalloc(size, GFP_ATOMIC); else sc_ret_msg = kmalloc(size, GFP_KERNEL); } if (sc_ret_msg == NULL) { if (size > PAGE_SIZE) sc_ret_msg = vmalloc(size); if (sc_ret_msg == NULL) return; } memset(sc_ret_msg, 0, size); scd_ctrl_cmd_from_user(data, sc_ret_msg); sc_nl_send_to_user(pid, seq, sc_ret_msg); kvfree(sc_ret_msg); } void sc_select_charging_current(struct charger_manager *info, struct charger_data *pdata) { chr_err("sck: en:%d pid:%d %d %d %d %d %d thermal.dis:%d\n", info->sc.enable, info->sc.g_scd_pid, info->sc.pre_ibat, info->sc.sc_ibat, pdata->charging_current_limit, pdata->thermal_charging_current_limit, info->sc.solution, pinfo->sc.disable_in_this_plug); if (pinfo->sc.g_scd_pid != 0 && pinfo->sc.disable_in_this_plug == false) { if (info->sc.pre_ibat == -1 || info->sc.solution == SC_IGNORE || info->sc.solution == SC_DISABLE) { info->sc.sc_ibat = -1; } else { if (info->sc.pre_ibat == pdata->charging_current_limit && info->sc.solution == SC_REDUCE && ((pdata->charging_current_limit - 100000) >= 500000)) { if (info->sc.sc_ibat == -1) info->sc.sc_ibat = pdata->charging_current_limit - 100000; else if (info->sc.sc_ibat - 100000 >= 500000) info->sc.sc_ibat = info->sc.sc_ibat - 100000; } } } info->sc.pre_ibat = pdata->charging_current_limit; if (pdata->thermal_charging_current_limit != -1) { if (pdata->thermal_charging_current_limit < pdata->charging_current_limit) pdata->charging_current_limit = pdata->thermal_charging_current_limit; pinfo->sc.disable_in_this_plug = true; } else if ((info->sc.solution == SC_REDUCE || info->sc.solution == SC_KEEP) && info->sc.sc_ibat < pdata->charging_current_limit && pinfo->sc.g_scd_pid != 0 && pinfo->sc.disable_in_this_plug == false && info->sc.sc_ibat != -1) { pdata->charging_current_limit = info->sc.sc_ibat; } } void sc_init(struct smartcharging *sc) { sc->enable = false; sc->start_time = 0; sc->end_time = 80000; sc->target_percentage = 80; sc->pre_ibat = -1; sc->bh = 100; chr_err("%s: en:%d time:%d,%d tsoc:%d %d %d %d\n", __func__, sc->enable, sc->start_time, sc->end_time, sc->target_percentage, sc->battery_size, sc->left_time_for_cv, sc->current_limit); } void sc_update(struct charger_manager *pinfo) { int time = pinfo->sc.left_time_for_cv; int bh = pinfo->sc.bh; memset(&pinfo->sc.data, 0, sizeof(struct scd_cmd_param_t_1)); pinfo->sc.data.data[SC_VBAT] = battery_get_bat_voltage(); pinfo->sc.data.data[SC_BAT_TMP] = battery_get_bat_temperature(); pinfo->sc.data.data[SC_UISOC] = battery_get_uisoc(); pinfo->sc.data.data[SC_SOC] = battery_get_soc(); if (bh <= 80) { pinfo->sc.enable = false; chr_err("battery health(%d) is too low to enable sc\n", bh); } pinfo->sc.data.data[SC_ENABLE] = pinfo->sc.enable; pinfo->sc.data.data[SC_BAT_SIZE] = pinfo->sc.battery_size; pinfo->sc.data.data[SC_START_TIME] = pinfo->sc.start_time; pinfo->sc.data.data[SC_END_TIME] = pinfo->sc.end_time; pinfo->sc.data.data[SC_IBAT_LIMIT] = pinfo->sc.current_limit; pinfo->sc.data.data[SC_TARGET_PERCENTAGE] = pinfo->sc.target_percentage; if (bh <= 90) { chr_err("battery health(%d) is low ,time from %d => %d\n", bh, time, time * 3 / 2); time = time * 3 / 2; } pinfo->sc.data.data[SC_LEFT_TIME_FOR_CV] = time; charger_dev_get_charging_current(pinfo->chg1_dev, &pinfo->sc.data.data[SC_IBAT_SETTING]); pinfo->sc.data.data[SC_IBAT_SETTING] = pinfo->sc.data.data[SC_IBAT_SETTING] / 1000; pinfo->sc.data.data[SC_IBAT] = battery_get_bat_current() / 10; charger_dev_get_ibus(pinfo->chg1_dev, &pinfo->sc.data.data[SC_IBUS]); if (chargerlog_level == 1) pinfo->sc.data.data[SC_DBGLV] = 3; else pinfo->sc.data.data[SC_DBGLV] = 7; } int wakeup_sc_algo_cmd(struct scd_cmd_param_t_1 *data, int subcmd, int para1) { if (pinfo->sc.g_scd_pid != 0) { struct sc_nl_msg_t *sc_msg; int size = SCD_NL_MSG_T_HDR_LEN + sizeof(struct scd_cmd_param_t_1); if (size > (PAGE_SIZE << 1)) sc_msg = vmalloc(size); else { if (in_interrupt()) sc_msg = kmalloc(size, GFP_ATOMIC); else sc_msg = kmalloc(size, GFP_KERNEL); } if (sc_msg == NULL) { if (size > PAGE_SIZE) sc_msg = vmalloc(size); if (sc_msg == NULL) return -1; } sc_update(pinfo); chr_debug( "[wakeup_fg_algo] malloc size=%d pid=%d\n", size, pinfo->sc.g_scd_pid); memset(sc_msg, 0, size); sc_msg->sc_cmd = SC_DAEMON_CMD_NOTIFY_DAEMON; sc_msg->sc_subcmd = subcmd; sc_msg->sc_subcmd_para1 = para1; memcpy(sc_msg->sc_data, data, sizeof(struct scd_cmd_param_t_1)); sc_msg->sc_data_len += sizeof(struct scd_cmd_param_t_1); sc_nl_send_to_user(pinfo->sc.g_scd_pid, 0, sc_msg); kvfree(sc_msg); return 0; } chr_debug("pid is NULL\n"); return -1; } static ssize_t show_sc_en( struct device *dev, struct device_attribute *attr, char *buf) { chr_err( "[enable smartcharging] : %d\n", pinfo->sc.enable); return sprintf(buf, "%d\n", pinfo->sc.enable); } static ssize_t store_sc_en( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[enable smartcharging] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[enable smartcharging] val is %d ??\n", (int)val); val = 0; } if (val == 0) pinfo->sc.enable = false; else pinfo->sc.enable = true; chr_err( "[enable smartcharging]enable smartcharging=%d\n", pinfo->sc.enable); } return size; } static DEVICE_ATTR(enable_sc, 0664, show_sc_en, store_sc_en); static ssize_t show_sc_stime( struct device *dev, struct device_attribute *attr, char *buf) { chr_err( "[smartcharging stime] : %d\n", pinfo->sc.start_time); return sprintf(buf, "%d\n", pinfo->sc.start_time); } static ssize_t store_sc_stime( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[smartcharging stime] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[smartcharging stime] val is %d ??\n", (int)val); val = 0; } if (val >= 0) pinfo->sc.start_time = val; chr_err( "[smartcharging stime]enable smartcharging=%d\n", pinfo->sc.start_time); } return size; } static DEVICE_ATTR(sc_stime, 0664, show_sc_stime, store_sc_stime); static ssize_t show_sc_etime( struct device *dev, struct device_attribute *attr, char *buf) { chr_err( "[smartcharging etime] : %d\n", pinfo->sc.end_time); return sprintf(buf, "%d\n", pinfo->sc.end_time); } static ssize_t store_sc_etime( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[smartcharging etime] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[smartcharging etime] val is %d ??\n", (int)val); val = 0; } if (val >= 0) pinfo->sc.end_time = val; chr_err( "[smartcharging stime]enable smartcharging=%d\n", pinfo->sc.end_time); } return size; } static DEVICE_ATTR(sc_etime, 0664, show_sc_etime, store_sc_etime); static ssize_t show_sc_tuisoc( struct device *dev, struct device_attribute *attr, char *buf) { chr_err( "[smartcharging target uisoc] : %d\n", pinfo->sc.target_percentage); return sprintf(buf, "%d\n", pinfo->sc.target_percentage); } static ssize_t store_sc_tuisoc( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[smartcharging tuisoc] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[smartcharging tuisoc] val is %d ??\n", (int)val); val = 0; } if (val >= 0) pinfo->sc.target_percentage = val; chr_err( "[smartcharging stime]tuisoc=%d\n", pinfo->sc.target_percentage); } return size; } static DEVICE_ATTR(sc_tuisoc, 0664, show_sc_tuisoc, store_sc_tuisoc); static ssize_t show_sc_ibat_limit( struct device *dev, struct device_attribute *attr, char *buf) { chr_err( "[smartcharging ibat limit] : %d\n", pinfo->sc.current_limit); return sprintf(buf, "%d\n", pinfo->sc.current_limit); } static ssize_t store_sc_ibat_limit( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[smartcharging ibat limit] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[smartcharging ibat limit] val is %d ??\n", (int)val); val = 0; } if (val >= 0) pinfo->sc.current_limit = val; chr_err( "[smartcharging ibat limit]=%d\n", pinfo->sc.current_limit); } return size; } static DEVICE_ATTR(sc_ibat_limit, 0664, show_sc_ibat_limit, store_sc_ibat_limit); static ssize_t show_sc_test( struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", 0); } static ssize_t store_sc_test( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; if (buf != NULL && size != 0) { chr_err("[smartcharging test] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (val < 0) { chr_err( "[smartcharging test] val is %d ??\n", (int)val); val = 0; } if (val == 1) { charger_manager_enable_sc(pinfo->chg1_consumer, true, 1, 1111); } else if (val == 2) { charger_manager_enable_sc(pinfo->chg1_consumer, false, 2, 2222); } else if (val == 3) { charger_manager_set_sc_current_limit(pinfo->chg1_consumer, 1000); } else { charger_manager_set_bh(pinfo->chg1_consumer, val); } } return size; } static DEVICE_ATTR(sc_test, 0664, show_sc_test, store_sc_test); static int mtk_charger_probe(struct platform_device *pdev) { struct charger_manager *info = NULL; struct list_head *pos = NULL; struct list_head *phead = &consumer_head; struct charger_consumer *ptr = NULL; int i, ret; int ret_device_file; struct netlink_kernel_cfg cfg = { .input = chg_nl_data_handler, }; #if !defined(CONFIG_BATTERY_SAMSUNG) struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT // workaround for mt6768 //int boot_mode = get_boot_mode(); dev = &(pdev->dev); if (dev != NULL){ boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node){ chr_err("%s: failed to get boot mode phandle\n", __func__); } else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag){ chr_err("%s: failed to get atag,boot\n", __func__); } else boot_mode = tag->bootmode; } } #endif chr_err("%s: starts\n", __func__); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; pinfo = info; platform_set_drvdata(pdev, info); info->pdev = pdev; mtk_charger_parse_dt(info, &pdev->dev); mutex_init(&info->charger_lock); mutex_init(&info->charger_pd_lock); mutex_init(&info->cable_out_lock); for (i = 0; i < TOTAL_CHARGER; i++) { mutex_init(&info->pp_lock[i]); info->force_disable_pp[i] = false; info->enable_pp[i] = true; } /*work around for mt6768*/ atomic_set(&info->enable_kpoc_shdn, 1); info->charger_wakelock = wakeup_source_register(NULL, "charger suspend wakelock"); spin_lock_init(&info->slock); /* init thread */ init_waitqueue_head(&info->wait_que); info->polling_interval = CHARGING_INTERVAL; info->enable_dynamic_cv = true; info->chg1_data.thermal_charging_current_limit = -1; info->chg1_data.thermal_input_current_limit = -1; info->chg1_data.input_current_limit_by_aicl = -1; info->chg2_data.thermal_charging_current_limit = -1; info->chg2_data.thermal_input_current_limit = -1; info->dvchg1_data.thermal_input_current_limit = -1; info->dvchg2_data.thermal_input_current_limit = -1; info->sw_jeita.error_recovery_flag = true; mtk_charger_init_timer(info); info->is_pdc_run = false; kthread_run(charger_routine_thread, info, "charger_thread"); if (info->chg1_dev != NULL && info->do_event != NULL) { info->chg1_nb.notifier_call = info->do_event; register_charger_device_notifier(info->chg1_dev, &info->chg1_nb); charger_dev_set_drvdata(info->chg1_dev, info); } info->psy_nb.notifier_call = charger_psy_event; power_supply_reg_notifier(&info->psy_nb); srcu_init_notifier_head(&info->evt_nh); ret = mtk_charger_setup_files(pdev); if (ret) chr_err("Error creating sysfs interface\n"); info->pd_adapter = get_adapter_by_name("pd_adapter"); if (info->pd_adapter) chr_err("Found PD adapter [%s]\n", info->pd_adapter->props.alias_name); else chr_err("*** Error : can't find PD adapter ***\n"); if (mtk_pe_init(info) < 0) info->enable_pe_plus = false; if (mtk_pe20_init(info) < 0) info->enable_pe_2 = false; if (mtk_pe40_init(info) == false) info->enable_pe_4 = false; if (mtk_pe50_init(info) < 0) info->enable_pe_5 = false; mtk_pdc_init(info); charger_ftm_init(); mtk_charger_get_atm_mode(info); sw_jeita_state_machine_init(info); #ifdef CONFIG_MTK_CHARGER_UNLIMITED info->usb_unlimited = true; info->enable_sw_safety_timer = false; charger_dev_enable_safety_timer(info->chg1_dev, false); #endif info->sc.daemo_nl_sk = netlink_kernel_create(&init_net, NETLINK_CHG, &cfg); if (info->sc.daemo_nl_sk == NULL) chr_err("sc netlink_kernel_create error id:%d\n", NETLINK_CHG); else chr_err("sc_netlink_kernel_create success id:%d\n", NETLINK_CHG); sc_init(&info->sc); charger_debug_init(); mutex_lock(&consumer_mutex); list_for_each(pos, phead) { ptr = container_of(pos, struct charger_consumer, list); ptr->cm = info; if (ptr->pnb != NULL) { srcu_notifier_chain_register(&info->evt_nh, ptr->pnb); ptr->pnb = NULL; } } mutex_unlock(&consumer_mutex); /* sysfs node */ ret_device_file = device_create_file(&(pdev->dev), &dev_attr_enable_sc); ret_device_file = device_create_file(&(pdev->dev), &dev_attr_sc_stime); ret_device_file = device_create_file(&(pdev->dev), &dev_attr_sc_etime); ret_device_file = device_create_file(&(pdev->dev), &dev_attr_sc_tuisoc); ret_device_file = device_create_file(&(pdev->dev), &dev_attr_sc_ibat_limit); ret_device_file = device_create_file(&(pdev->dev), &dev_attr_sc_test); info->chg1_consumer = charger_manager_get_by_name(&pdev->dev, "charger_port1"); #if !defined(CONFIG_BATTERY_SAMSUNG) if (info->chg1_consumer != NULL && boot_mode != KERNEL_POWER_OFF_CHARGING_BOOT && boot_mode != LOW_POWER_OFF_CHARGING_BOOT) charger_manager_force_disable_power_path( info->chg1_consumer, MAIN_CHARGER, true); #endif info->init_done = true; _wake_up_charger(info); return 0; } static int mtk_charger_remove(struct platform_device *dev) { struct charger_manager *info = platform_get_drvdata(dev); mtk_pe50_deinit(info); return 0; } static void mtk_charger_shutdown(struct platform_device *dev) { struct charger_manager *info = platform_get_drvdata(dev); int ret; if (mtk_pe20_get_is_connect(info) || mtk_pe_get_is_connect(info)) { if (info->chg2_dev) charger_dev_enable(info->chg2_dev, false); ret = mtk_pe20_reset_ta_vchr(info); if (ret == -ENOTSUPP) mtk_pe_reset_ta_vchr(info); pr_debug("%s: reset TA before shutdown\n", __func__); } } static const struct of_device_id mtk_charger_of_match[] = { {.compatible = "mediatek,charger",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_charger_of_match); struct platform_device charger_device = { .name = "charger", .id = -1, }; static struct platform_driver charger_driver = { .probe = mtk_charger_probe, .remove = mtk_charger_remove, .shutdown = mtk_charger_shutdown, .driver = { .name = "charger", .of_match_table = mtk_charger_of_match, }, }; static int __init mtk_charger_init(void) { return platform_driver_register(&charger_driver); } late_initcall(mtk_charger_init); static void __exit mtk_charger_exit(void) { platform_driver_unregister(&charger_driver); } module_exit(mtk_charger_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK Charger Driver"); MODULE_LICENSE("GPL");