4526 lines
123 KiB
C
Executable file
4526 lines
123 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Filename:
|
|
* ---------
|
|
* mtk_charger.c
|
|
*
|
|
* Project:
|
|
* --------
|
|
* Android_Software
|
|
*
|
|
* Description:
|
|
* ------------
|
|
* This Module defines functions of Battery charging
|
|
*
|
|
* Author:
|
|
* -------
|
|
* Wy Chuang
|
|
*
|
|
*/
|
|
#include <linux/init.h> /* For init/exit macros */
|
|
#include <linux/module.h> /* For MODULE_ marcros */
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/pm_wakeup.h>
|
|
#include <linux/rtc.h>
|
|
#include <linux/time.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/reboot.h>
|
|
|
|
#include <asm/setup.h>
|
|
|
|
#include "mtk_charger.h"
|
|
#include "mtk_battery.h"
|
|
|
|
struct tag_bootmode {
|
|
u32 size;
|
|
u32 tag;
|
|
u32 bootmode;
|
|
u32 boottype;
|
|
};
|
|
|
|
#ifdef MODULE
|
|
static char __chg_cmdline[COMMAND_LINE_SIZE];
|
|
static char *chg_cmdline = __chg_cmdline;
|
|
|
|
const char *chg_get_cmd(void)
|
|
{
|
|
struct device_node *of_chosen = NULL;
|
|
char *bootargs = NULL;
|
|
|
|
if (__chg_cmdline[0] != 0)
|
|
return chg_cmdline;
|
|
|
|
of_chosen = of_find_node_by_path("/chosen");
|
|
if (of_chosen) {
|
|
bootargs = (char *)of_get_property(
|
|
of_chosen, "bootargs", NULL);
|
|
if (!bootargs)
|
|
chr_err("%s: failed to get bootargs\n", __func__);
|
|
else {
|
|
strcpy(__chg_cmdline, bootargs);
|
|
chr_err("%s: bootargs: %s\n", __func__, bootargs);
|
|
}
|
|
} else
|
|
chr_err("%s: failed to get /chosen\n", __func__);
|
|
|
|
return chg_cmdline;
|
|
}
|
|
|
|
#else
|
|
const char *chg_get_cmd(void)
|
|
{
|
|
return saved_command_line;
|
|
}
|
|
#endif
|
|
|
|
int chr_get_debug_level(void)
|
|
{
|
|
struct power_supply *psy;
|
|
static struct mtk_charger *info;
|
|
int ret;
|
|
|
|
if (info == NULL) {
|
|
psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (psy == NULL)
|
|
ret = CHRLOG_DEBUG_LEVEL;
|
|
else {
|
|
info =
|
|
(struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (info == NULL)
|
|
ret = CHRLOG_DEBUG_LEVEL;
|
|
else
|
|
ret = info->log_level;
|
|
}
|
|
} else
|
|
ret = info->log_level;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(chr_get_debug_level);
|
|
|
|
void _wake_up_charger(struct mtk_charger *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);
|
|
}
|
|
|
|
bool is_disable_charger(struct mtk_charger *info)
|
|
{
|
|
if (info == NULL)
|
|
return true;
|
|
|
|
if (info->disable_charger == true || IS_ENABLED(CONFIG_POWER_EXT))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
int _mtk_enable_charging(struct mtk_charger *info,
|
|
bool en)
|
|
{
|
|
chr_debug("%s en:%d\n", __func__, en);
|
|
if (info->algo.enable_charging != NULL)
|
|
return info->algo.enable_charging(info, en);
|
|
return false;
|
|
}
|
|
|
|
int mtk_charger_notifier(struct mtk_charger *info, int event)
|
|
{
|
|
return srcu_notifier_call_chain(&info->evt_nh, event, NULL);
|
|
}
|
|
|
|
static void mtk_charger_parse_dt(struct mtk_charger *info,
|
|
struct device *dev)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
u32 val = 0;
|
|
struct device_node *boot_node = NULL;
|
|
struct tag_bootmode *tag = 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 {
|
|
chr_err("%s: size:0x%x tag:0x%x bootmode:0x%x boottype:0x%x\n",
|
|
__func__, tag->size, tag->tag,
|
|
tag->bootmode, tag->boottype);
|
|
info->bootmode = tag->bootmode;
|
|
info->boottype = tag->boottype;
|
|
}
|
|
}
|
|
|
|
if (of_property_read_string(np, "algorithm_name",
|
|
&info->algorithm_name) < 0) {
|
|
chr_err("%s: no algorithm_name name\n", __func__);
|
|
info->algorithm_name = "Basic";
|
|
}
|
|
|
|
if (strcmp(info->algorithm_name, "Basic") == 0) {
|
|
chr_err("found Basic\n");
|
|
mtk_basic_charger_init(info);
|
|
} else if (strcmp(info->algorithm_name, "Pulse") == 0) {
|
|
chr_err("found Pulse\n");
|
|
mtk_pulse_charger_init(info);
|
|
}
|
|
|
|
info->disable_charger = of_property_read_bool(np, "disable_charger");
|
|
info->charger_unlimited = of_property_read_bool(np, "charger_unlimited");
|
|
info->atm_enabled = of_property_read_bool(np, "atm_is_enabled");
|
|
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->disable_aicl = of_property_read_bool(np, "disable_aicl");
|
|
info->alg_new_arbitration = of_property_read_bool(np, "alg_new_arbitration");
|
|
info->alg_unchangeable = of_property_read_bool(np, "alg_unchangeable");
|
|
|
|
/* common */
|
|
|
|
if (of_property_read_u32(np, "charger_configuration", &val) >= 0)
|
|
info->config = val;
|
|
else {
|
|
chr_err("use default charger_configuration:%d\n",
|
|
SINGLE_CHARGER);
|
|
info->config = SINGLE_CHARGER;
|
|
}
|
|
|
|
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, "vbus_sw_ovp_voltage", &val) >= 0)
|
|
info->data.vbus_sw_ovp_voltage = val;
|
|
else {
|
|
chr_err("use default V_CHARGER_MAX:%d\n", V_CHARGER_MAX);
|
|
info->data.vbus_sw_ovp_voltage = VBUS_OVP_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;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "enable_vbat_mon", &val) >= 0) {
|
|
info->enable_vbat_mon = val;
|
|
info->enable_vbat_mon_bak = val;
|
|
} else if (of_property_read_u32(np, "enable-vbat-mon", &val) >= 0) {
|
|
info->enable_vbat_mon = val;
|
|
info->enable_vbat_mon_bak = val;
|
|
} else {
|
|
chr_err("use default enable 6pin\n");
|
|
info->enable_vbat_mon = 0;
|
|
info->enable_vbat_mon_bak = 0;
|
|
}
|
|
chr_err("use 6pin bat, enable_vbat_mon:%d\n", info->enable_vbat_mon);
|
|
|
|
/* sw jeita */
|
|
info->enable_sw_jeita = of_property_read_bool(np, "enable_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;
|
|
}
|
|
|
|
/*prize LiuYong, modify for charging current config, 20230323 -start*/
|
|
if (of_property_read_u32(np, "jeita_temp_t0_to_t1_input_current", &val) >= 0)
|
|
info->data.jeita_temp_t0_to_t1_input_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t0_to_t1_input_current:%d\n",
|
|
JEITA_TEMP_T0_TO_T1_INPUT_CURRENT);
|
|
info->data.jeita_temp_t0_to_t1_input_current = JEITA_TEMP_T0_TO_T1_INPUT_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t0_to_t1_charging_current", &val) >= 0)
|
|
info->data.jeita_temp_t0_to_t1_charging_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t0_to_t1_charging_current:%d\n",
|
|
JEITA_TEMP_T0_TO_T1_CHARGING_CURRENT);
|
|
info->data.jeita_temp_t0_to_t1_charging_current = JEITA_TEMP_T0_TO_T1_CHARGING_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t1_to_t2_input_current", &val) >= 0)
|
|
info->data.jeita_temp_t1_to_t2_input_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t1_to_t2_input_current:%d\n",
|
|
JEITA_TEMP_T1_TO_T2_INPUT_CURRENT);
|
|
info->data.jeita_temp_t1_to_t2_input_current = JEITA_TEMP_T1_TO_T2_INPUT_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t1_to_t2_charging_current", &val) >= 0)
|
|
info->data.jeita_temp_t1_to_t2_charging_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t1_to_t2_charging_current:%d\n",
|
|
JEITA_TEMP_T1_TO_T2_CHARGING_CURRENT);
|
|
info->data.jeita_temp_t1_to_t2_charging_current = JEITA_TEMP_T1_TO_T2_CHARGING_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t2_to_t3_input_current", &val) >= 0)
|
|
info->data.jeita_temp_t2_to_t3_input_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t2_to_t3_input_current:%d\n",
|
|
JEITA_TEMP_T2_TO_T3_INPUT_CURRENT);
|
|
info->data.jeita_temp_t2_to_t3_input_current = JEITA_TEMP_T2_TO_T3_INPUT_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t2_to_t3_charging_current", &val) >= 0)
|
|
info->data.jeita_temp_t2_to_t3_charging_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t2_to_t3_charging_current:%d\n",
|
|
JEITA_TEMP_T2_TO_T3_CHARGING_CURRENT);
|
|
info->data.jeita_temp_t2_to_t3_charging_current = JEITA_TEMP_T2_TO_T3_CHARGING_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t3_to_t4_input_current", &val) >= 0)
|
|
info->data.jeita_temp_t3_to_t4_input_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t3_to_t4_input_current:%d\n",
|
|
JEITA_TEMP_T3_TO_T4_INPUT_CURRENT);
|
|
info->data.jeita_temp_t3_to_t4_input_current = JEITA_TEMP_T3_TO_T4_INPUT_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "jeita_temp_t3_to_t4_charging_current", &val) >= 0)
|
|
info->data.jeita_temp_t3_to_t4_charging_current = val;
|
|
else {
|
|
chr_err("use default jeita_temp_t3_to_t4_charging_current:%d\n",
|
|
JEITA_TEMP_T3_TO_T4_CHARGING_CURRENT);
|
|
info->data.jeita_temp_t3_to_t4_charging_current = JEITA_TEMP_T3_TO_T4_CHARGING_CURRENT;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "temp_screen_on_input_current", &val) >= 0)
|
|
info->data.temp_screen_on_input_current = val;
|
|
else {
|
|
chr_err("use default temp_screen_on_input_current:%d\n",
|
|
1500000);
|
|
info->data.temp_screen_on_input_current = 1500000;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "temp_screen_on_charging_current", &val) >= 0)
|
|
info->data.temp_screen_on_charging_current = val;
|
|
else {
|
|
chr_err("use default temp_screen_on_charging_current:%d\n",
|
|
1500000);
|
|
info->data.temp_screen_on_charging_current = 1500000;
|
|
}
|
|
/*prize LiuYong, modify for charging current config, 20230323 -end*/
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* charging current */
|
|
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;
|
|
}
|
|
|
|
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, "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;
|
|
}
|
|
|
|
/* dynamic mivr */
|
|
info->enable_dynamic_mivr =
|
|
of_property_read_bool(np, "enable_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;
|
|
}
|
|
/* fast charging algo support indicator */
|
|
info->enable_fast_charging_indicator =
|
|
of_property_read_bool(np, "enable_fast_charging_indicator");
|
|
}
|
|
|
|
static void mtk_charger_start_timer(struct mtk_charger *info)
|
|
{
|
|
struct timespec64 end_time, time_now;
|
|
ktime_t ktime, ktime_now;
|
|
int ret = 0;
|
|
|
|
/* If the timer was already set, cancel it */
|
|
ret = alarm_try_to_cancel(&info->charger_timer);
|
|
if (ret < 0) {
|
|
chr_err("%s: callback was running, skip timer\n", __func__);
|
|
return;
|
|
}
|
|
|
|
ktime_now = ktime_get_boottime();
|
|
time_now = ktime_to_timespec64(ktime_now);
|
|
end_time.tv_sec = time_now.tv_sec + info->polling_interval;
|
|
end_time.tv_nsec = time_now.tv_nsec + 0;
|
|
info->endtime = end_time;
|
|
ktime = ktime_set(info->endtime.tv_sec, info->endtime.tv_nsec);
|
|
|
|
chr_err("%s: alarm timer start:%d, %ld %ld\n", __func__, ret,
|
|
info->endtime.tv_sec, info->endtime.tv_nsec);
|
|
alarm_start(&info->charger_timer, ktime);
|
|
}
|
|
|
|
static void check_battery_exist(struct mtk_charger *info)
|
|
{
|
|
unsigned int i = 0;
|
|
int count = 0;
|
|
//int boot_mode = get_boot_mode();
|
|
|
|
if (is_disable_charger(info))
|
|
return;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (is_battery_exist(info) == false)
|
|
count++;
|
|
}
|
|
|
|
#ifdef FIXME
|
|
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);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void check_dynamic_mivr(struct mtk_charger *info)
|
|
{
|
|
int i = 0, ret = 0;
|
|
int vbat = 0;
|
|
bool is_fast_charge = false;
|
|
struct chg_alg_device *alg = NULL;
|
|
|
|
if (!info->enable_dynamic_mivr)
|
|
return;
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if (ret == ALG_RUNNING) {
|
|
is_fast_charge = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!is_fast_charge) {
|
|
vbat = get_battery_voltage(info);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* sw jeita */
|
|
void do_sw_jeita_state_machine(struct mtk_charger *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 int mtk_chgstat_notify(struct mtk_charger *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;
|
|
}
|
|
|
|
static void mtk_charger_set_algo_log_level(struct mtk_charger *info, int level)
|
|
{
|
|
struct chg_alg_device *alg;
|
|
int i = 0, ret = 0;
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
|
|
ret = chg_alg_set_prop(alg, ALG_LOG_LEVEL, level);
|
|
if (ret < 0)
|
|
chr_err("%s: set ALG_LOG_LEVEL fail, ret =%d", __func__, ret);
|
|
}
|
|
}
|
|
|
|
static ssize_t sw_jeita_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *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 sw_jeita_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *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;
|
|
|
|
} else {
|
|
chr_err("%s: format error!\n", __func__);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(sw_jeita);
|
|
/* sw jeita end*/
|
|
|
|
static ssize_t sw_ovp_threshold_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_err("%s: %d\n", __func__, pinfo->data.max_charger_voltage);
|
|
return sprintf(buf, "%d\n", pinfo->data.max_charger_voltage);
|
|
}
|
|
|
|
static ssize_t sw_ovp_threshold_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
signed int temp;
|
|
|
|
if (kstrtoint(buf, 10, &temp) == 0) {
|
|
if (temp < 0)
|
|
pinfo->data.max_charger_voltage = pinfo->data.vbus_sw_ovp_voltage;
|
|
else
|
|
pinfo->data.max_charger_voltage = temp;
|
|
chr_err("%s: %d\n", __func__, pinfo->data.max_charger_voltage);
|
|
|
|
} else {
|
|
chr_err("%s: format error!\n", __func__);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(sw_ovp_threshold);
|
|
|
|
static ssize_t chr_type_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_err("%s: %d\n", __func__, pinfo->chr_type);
|
|
return sprintf(buf, "%d\n", pinfo->chr_type);
|
|
}
|
|
|
|
static ssize_t chr_type_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
signed int temp;
|
|
|
|
if (kstrtoint(buf, 10, &temp) == 0)
|
|
pinfo->chr_type = temp;
|
|
else
|
|
chr_err("%s: format error!\n", __func__);
|
|
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(chr_type);
|
|
|
|
static ssize_t pd_type_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
char *pd_type_name = "None";
|
|
|
|
switch (pinfo->pd_type) {
|
|
case MTK_PD_CONNECT_NONE:
|
|
pd_type_name = "None";
|
|
break;
|
|
case MTK_PD_CONNECT_PE_READY_SNK:
|
|
pd_type_name = "PD";
|
|
break;
|
|
case MTK_PD_CONNECT_PE_READY_SNK_PD30:
|
|
pd_type_name = "PD";
|
|
break;
|
|
case MTK_PD_CONNECT_PE_READY_SNK_APDO:
|
|
pd_type_name = "PD with PPS";
|
|
break;
|
|
case MTK_PD_CONNECT_TYPEC_ONLY_SNK:
|
|
pd_type_name = "normal";
|
|
break;
|
|
}
|
|
chr_err("%s: %d\n", __func__, pinfo->pd_type);
|
|
return sprintf(buf, "%s\n", pd_type_name);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(pd_type);
|
|
|
|
|
|
static ssize_t Pump_Express_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int ret = 0, i = 0;
|
|
bool is_ta_detected = false;
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
struct chg_alg_device *alg = NULL;
|
|
|
|
if (!pinfo) {
|
|
chr_err("%s: pinfo is null\n", __func__);
|
|
return sprintf(buf, "%d\n", is_ta_detected);
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = pinfo->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if (ret == ALG_RUNNING) {
|
|
is_ta_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
chr_err("%s: idx = %d, detect = %d\n", __func__, i, is_ta_detected);
|
|
return sprintf(buf, "%d\n", is_ta_detected);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(Pump_Express);
|
|
|
|
static ssize_t Charging_mode_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int ret = 0, i = 0;
|
|
char *alg_name = "normal";
|
|
bool is_ta_detected = false;
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
struct chg_alg_device *alg = NULL;
|
|
|
|
if (!pinfo) {
|
|
chr_err("%s: pinfo is null\n", __func__);
|
|
return sprintf(buf, "%d\n", is_ta_detected);
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = pinfo->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if (ret == ALG_RUNNING) {
|
|
is_ta_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
if (alg == NULL)
|
|
return sprintf(buf, "%s\n", alg_name);
|
|
|
|
switch (alg->alg_id) {
|
|
case PE_ID:
|
|
alg_name = "PE";
|
|
break;
|
|
case PE2_ID:
|
|
alg_name = "PE2";
|
|
break;
|
|
case PDC_ID:
|
|
alg_name = "PDC";
|
|
break;
|
|
case PE4_ID:
|
|
alg_name = "PE4";
|
|
break;
|
|
case PE5_ID:
|
|
alg_name = "P5";
|
|
break;
|
|
case PE5P_ID:
|
|
alg_name = "P5P";
|
|
break;
|
|
}
|
|
chr_err("%s: charging_mode: %s\n", __func__, alg_name);
|
|
return sprintf(buf, "%s\n", alg_name);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(Charging_mode);
|
|
|
|
static ssize_t High_voltage_chg_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_err("%s: hv_charging = %d\n", __func__, pinfo->enable_hv_charging);
|
|
return sprintf(buf, "%d\n", pinfo->enable_hv_charging);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(High_voltage_chg_enable);
|
|
|
|
static ssize_t Rust_detect_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_err("%s: Rust detect = %d\n", __func__, pinfo->record_water_detected);
|
|
return sprintf(buf, "%d\n", pinfo->record_water_detected);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(Rust_detect);
|
|
|
|
static ssize_t Thermal_throttle_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
struct charger_data *chg_data = &(pinfo->chg_data[CHG1_SETTING]);
|
|
|
|
return sprintf(buf, "%d\n", chg_data->thermal_throttle_record);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(Thermal_throttle);
|
|
|
|
static ssize_t fast_chg_indicator_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_debug("%s: %d\n", __func__, pinfo->fast_charging_indicator);
|
|
return sprintf(buf, "%d\n", pinfo->fast_charging_indicator);
|
|
}
|
|
|
|
static ssize_t fast_chg_indicator_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int temp;
|
|
|
|
if (kstrtouint(buf, 10, &temp) == 0)
|
|
pinfo->fast_charging_indicator = temp;
|
|
else
|
|
chr_err("%s: format error!\n", __func__);
|
|
|
|
if ((pinfo->fast_charging_indicator > 0) &&
|
|
(pinfo->bootmode == 8 || pinfo->bootmode == 9)) {
|
|
pinfo->log_level = CHRLOG_DEBUG_LEVEL;
|
|
mtk_charger_set_algo_log_level(pinfo, pinfo->log_level);
|
|
}
|
|
|
|
_wake_up_charger(pinfo);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(fast_chg_indicator);
|
|
|
|
static ssize_t alg_new_arbitration_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_debug("%s: %d\n", __func__, pinfo->alg_new_arbitration);
|
|
return sprintf(buf, "%d\n", pinfo->alg_new_arbitration);
|
|
}
|
|
|
|
static ssize_t alg_new_arbitration_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int temp;
|
|
|
|
if (kstrtouint(buf, 10, &temp) == 0)
|
|
pinfo->alg_new_arbitration = temp;
|
|
else
|
|
chr_err("%s: format error!\n", __func__);
|
|
|
|
_wake_up_charger(pinfo);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(alg_new_arbitration);
|
|
|
|
static ssize_t alg_unchangeable_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_debug("%s: %d\n", __func__, pinfo->alg_unchangeable);
|
|
return sprintf(buf, "%d\n", pinfo->alg_unchangeable);
|
|
}
|
|
|
|
static ssize_t alg_unchangeable_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int temp;
|
|
|
|
if (kstrtouint(buf, 10, &temp) == 0)
|
|
pinfo->alg_unchangeable = temp;
|
|
else
|
|
chr_err("%s: format error!\n", __func__);
|
|
|
|
_wake_up_charger(pinfo);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(alg_unchangeable);
|
|
|
|
static ssize_t enable_meta_current_limit_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_debug("%s: %d\n", __func__, pinfo->enable_meta_current_limit);
|
|
return sprintf(buf, "%d\n", pinfo->enable_meta_current_limit);
|
|
}
|
|
|
|
static ssize_t enable_meta_current_limit_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int temp;
|
|
|
|
if (kstrtouint(buf, 10, &temp) == 0)
|
|
pinfo->enable_meta_current_limit = temp;
|
|
else
|
|
chr_err("%s: format error!\n", __func__);
|
|
|
|
if (pinfo->enable_meta_current_limit > 0) {
|
|
pinfo->log_level = CHRLOG_DEBUG_LEVEL;
|
|
mtk_charger_set_algo_log_level(pinfo, pinfo->log_level);
|
|
}
|
|
|
|
_wake_up_charger(pinfo);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(enable_meta_current_limit);
|
|
|
|
static ssize_t vbat_mon_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_debug("%s: %d\n", __func__, pinfo->enable_vbat_mon);
|
|
return sprintf(buf, "%d\n", pinfo->enable_vbat_mon);
|
|
}
|
|
|
|
static ssize_t vbat_mon_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int temp;
|
|
|
|
if (kstrtouint(buf, 10, &temp) == 0) {
|
|
if (temp == 0)
|
|
pinfo->enable_vbat_mon = false;
|
|
else
|
|
pinfo->enable_vbat_mon = true;
|
|
} else {
|
|
chr_err("%s: format error!\n", __func__);
|
|
}
|
|
|
|
_wake_up_charger(pinfo);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(vbat_mon);
|
|
|
|
static ssize_t ADC_Charger_Voltage_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
int vbus = get_vbus(pinfo); /* mV */
|
|
|
|
chr_err("%s: %d\n", __func__, vbus);
|
|
return sprintf(buf, "%d\n", vbus);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(ADC_Charger_Voltage);
|
|
|
|
static ssize_t ADC_Charging_Current_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
int ibat = get_battery_current(pinfo); /* mA */
|
|
|
|
chr_err("%s: %d\n", __func__, ibat);
|
|
return sprintf(buf, "%d\n", ibat);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(ADC_Charging_Current);
|
|
|
|
static ssize_t input_current_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
int aicr = 0;
|
|
|
|
aicr = pinfo->chg_data[CHG1_SETTING].thermal_input_current_limit;
|
|
chr_err("%s: %d\n", __func__, aicr);
|
|
return sprintf(buf, "%d\n", aicr);
|
|
}
|
|
|
|
static ssize_t input_current_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
struct charger_data *chg_data;
|
|
signed int temp;
|
|
|
|
chg_data = &pinfo->chg_data[CHG1_SETTING];
|
|
if (kstrtoint(buf, 10, &temp) == 0) {
|
|
if (temp < 0)
|
|
chg_data->thermal_input_current_limit = 0;
|
|
else
|
|
chg_data->thermal_input_current_limit = temp;
|
|
} else {
|
|
chr_err("%s: format error!\n", __func__);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(input_current);
|
|
|
|
static ssize_t charger_log_level_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_err("%s: %d\n", __func__, pinfo->log_level);
|
|
return sprintf(buf, "%d\n", pinfo->log_level);
|
|
}
|
|
|
|
static ssize_t charger_log_level_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
int temp;
|
|
|
|
if (kstrtoint(buf, 10, &temp) == 0) {
|
|
if (temp < 0) {
|
|
chr_err("%s: val is invalid: %d\n", __func__, temp);
|
|
temp = 0;
|
|
}
|
|
pinfo->log_level = temp;
|
|
chr_err("%s: log_level=%d\n", __func__, pinfo->log_level);
|
|
|
|
mtk_charger_set_algo_log_level(pinfo, pinfo->log_level);
|
|
_wake_up_charger(pinfo);
|
|
|
|
} else {
|
|
chr_err("%s: format error!\n", __func__);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(charger_log_level);
|
|
|
|
static ssize_t BatteryNotify_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
|
|
chr_info("%s: 0x%x\n", __func__, pinfo->notify_code);
|
|
|
|
return sprintf(buf, "%u\n", pinfo->notify_code);
|
|
}
|
|
|
|
static ssize_t BatteryNotify_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int reg = 0;
|
|
int ret = 0;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
ret = kstrtouint(buf, 16, ®);
|
|
if (ret < 0) {
|
|
chr_err("%s: failed, ret = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
pinfo->notify_code = reg;
|
|
chr_info("%s: store code=0x%x\n", __func__, pinfo->notify_code);
|
|
mtk_chgstat_notify(pinfo);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(BatteryNotify);
|
|
|
|
/* procfs */
|
|
static int mtk_chg_set_cv_show(struct seq_file *m, void *data)
|
|
{
|
|
struct mtk_charger *pinfo = m->private;
|
|
|
|
seq_printf(m, "%d\n", pinfo->data.battery_cv);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_chg_set_cv_open(struct inode *node, struct file *file)
|
|
{
|
|
return single_open(file, mtk_chg_set_cv_show, PDE_DATA(node));
|
|
}
|
|
|
|
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 mtk_charger *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 >= BATTERY_CV) {
|
|
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;
|
|
}
|
|
|
|
static const struct proc_ops mtk_chg_set_cv_fops = {
|
|
.proc_open = mtk_chg_set_cv_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = mtk_chg_set_cv_write,
|
|
};
|
|
|
|
static int mtk_chg_current_cmd_show(struct seq_file *m, void *data)
|
|
{
|
|
struct mtk_charger *pinfo = m->private;
|
|
|
|
seq_printf(m, "%d %d\n", pinfo->usb_unlimited, pinfo->cmd_discharging);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_chg_current_cmd_open(struct inode *node, struct file *file)
|
|
{
|
|
return single_open(file, mtk_chg_current_cmd_show, PDE_DATA(node));
|
|
}
|
|
|
|
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 mtk_charger *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_dev_do_event(info->chg1_dev,
|
|
EVENT_DISCHARGE, 0);
|
|
} else if (cmd_discharging == 0) {
|
|
info->cmd_discharging = false;
|
|
charger_dev_enable(info->chg1_dev, true);
|
|
charger_dev_do_event(info->chg1_dev,
|
|
EVENT_RECHARGE, 0);
|
|
}
|
|
|
|
chr_info("%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 const struct proc_ops mtk_chg_current_cmd_fops = {
|
|
.proc_open = mtk_chg_current_cmd_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = mtk_chg_current_cmd_write,
|
|
};
|
|
|
|
static int mtk_chg_en_power_path_show(struct seq_file *m, void *data)
|
|
{
|
|
struct mtk_charger *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 int mtk_chg_en_power_path_open(struct inode *node, struct file *file)
|
|
{
|
|
return single_open(file, mtk_chg_en_power_path_show, PDE_DATA(node));
|
|
}
|
|
|
|
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 mtk_charger *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_powerpath(info->chg1_dev, enable);
|
|
chr_info("%s: enable power path = %d\n", __func__, enable);
|
|
return count;
|
|
}
|
|
|
|
chr_err("bad argument, echo [enable] > en_power_path\n");
|
|
return count;
|
|
}
|
|
|
|
static const struct proc_ops mtk_chg_en_power_path_fops = {
|
|
.proc_open = mtk_chg_en_power_path_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = mtk_chg_en_power_path_write,
|
|
};
|
|
|
|
static int mtk_chg_en_safety_timer_show(struct seq_file *m, void *data)
|
|
{
|
|
struct mtk_charger *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 int mtk_chg_en_safety_timer_open(struct inode *node, struct file *file)
|
|
{
|
|
return single_open(file, mtk_chg_en_safety_timer_show, PDE_DATA(node));
|
|
}
|
|
|
|
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 mtk_charger *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);
|
|
info->safety_timer_cmd = (int)enable;
|
|
chr_info("%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 const struct proc_ops mtk_chg_en_safety_timer_fops = {
|
|
.proc_open = mtk_chg_en_safety_timer_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
.proc_write = mtk_chg_en_safety_timer_write,
|
|
};
|
|
|
|
int sc_get_sys_time(void)
|
|
{
|
|
struct rtc_time tm_android = {0};
|
|
struct timespec64 tv_android = {0};
|
|
int timep = 0;
|
|
|
|
ktime_get_real_ts64(&tv_android);
|
|
rtc_time64_to_tm(tv_android.tv_sec, &tm_android);
|
|
tv_android.tv_sec -= (uint64_t)sys_tz.tz_minuteswest * 60;
|
|
rtc_time64_to_tm(tv_android.tv_sec, &tm_android);
|
|
timep = tm_android.tm_sec + tm_android.tm_min * 60 + tm_android.tm_hour * 3600;
|
|
|
|
return timep;
|
|
}
|
|
|
|
int sc_get_left_time(int s, int e, int now)
|
|
{
|
|
if (e >= s) {
|
|
if (now >= s && now < e)
|
|
return e-now;
|
|
} else {
|
|
if (now >= s)
|
|
return 86400 - now + e;
|
|
else if (now < e)
|
|
return e-now;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char *sc_solToStr(int s)
|
|
{
|
|
switch (s) {
|
|
case SC_IGNORE:
|
|
return "ignore";
|
|
case SC_KEEP:
|
|
return "keep";
|
|
case SC_DISABLE:
|
|
return "disable";
|
|
case SC_REDUCE:
|
|
return "reduce";
|
|
default:
|
|
return "none";
|
|
}
|
|
}
|
|
|
|
int smart_charging(struct mtk_charger *info)
|
|
{
|
|
int time_to_target = 0;
|
|
int time_to_full_default_current = -1;
|
|
int time_to_full_default_current_limit = -1;
|
|
int ret_value = SC_KEEP;
|
|
int sc_real_time = sc_get_sys_time();
|
|
int sc_left_time = sc_get_left_time(info->sc.start_time, info->sc.end_time, sc_real_time);
|
|
int sc_battery_percentage = get_uisoc(info) * 100;
|
|
int sc_charger_current = get_battery_current(info);
|
|
|
|
time_to_target = sc_left_time - info->sc.left_time_for_cv;
|
|
|
|
if (info->sc.enable == false || sc_left_time <= 0
|
|
|| sc_left_time < info->sc.left_time_for_cv
|
|
|| (sc_charger_current <= 0 && info->sc.last_solution != SC_DISABLE))
|
|
ret_value = SC_IGNORE;
|
|
else {
|
|
if (sc_battery_percentage > info->sc.target_percentage * 100) {
|
|
if (time_to_target > 0)
|
|
ret_value = SC_DISABLE;
|
|
} else {
|
|
if (sc_charger_current != 0)
|
|
time_to_full_default_current =
|
|
info->sc.battery_size * 3600 / 10000 *
|
|
(10000 - sc_battery_percentage)
|
|
/ sc_charger_current;
|
|
else
|
|
time_to_full_default_current =
|
|
info->sc.battery_size * 3600 / 10000 *
|
|
(10000 - sc_battery_percentage);
|
|
chr_err("sc1: %d %d %d %d %d\n",
|
|
time_to_full_default_current,
|
|
info->sc.battery_size,
|
|
sc_battery_percentage,
|
|
sc_charger_current,
|
|
info->sc.current_limit);
|
|
|
|
if (time_to_full_default_current < time_to_target &&
|
|
info->sc.current_limit > 0 &&
|
|
sc_charger_current > info->sc.current_limit) {
|
|
time_to_full_default_current_limit =
|
|
info->sc.battery_size / 10000 *
|
|
(10000 - sc_battery_percentage)
|
|
/ info->sc.current_limit;
|
|
|
|
chr_err("sc2: %d %d %d %d\n",
|
|
time_to_full_default_current_limit,
|
|
info->sc.battery_size,
|
|
sc_battery_percentage,
|
|
info->sc.current_limit);
|
|
|
|
if (time_to_full_default_current_limit < time_to_target &&
|
|
sc_charger_current > info->sc.current_limit)
|
|
ret_value = SC_REDUCE;
|
|
}
|
|
}
|
|
}
|
|
info->sc.last_solution = ret_value;
|
|
if (info->sc.last_solution == SC_DISABLE)
|
|
info->sc.disable_charger = true;
|
|
else
|
|
info->sc.disable_charger = false;
|
|
chr_err("[sc]disable_charger: %d\n", info->sc.disable_charger);
|
|
chr_err("[sc1]en:%d t:%d,%d,%d,%d t:%d,%d,%d,%d c:%d,%d ibus:%d uisoc: %d,%d s:%d ans:%s\n",
|
|
info->sc.enable, info->sc.start_time, info->sc.end_time,
|
|
sc_real_time, sc_left_time, info->sc.left_time_for_cv,
|
|
time_to_target, time_to_full_default_current, time_to_full_default_current_limit,
|
|
sc_charger_current, info->sc.current_limit,
|
|
get_ibus(info), get_uisoc(info), info->sc.target_percentage,
|
|
info->sc.battery_size, sc_solToStr(info->sc.last_solution));
|
|
|
|
return ret_value;
|
|
}
|
|
|
|
void sc_select_charging_current(struct mtk_charger *info, struct charger_data *pdata)
|
|
{
|
|
if (info->bootmode == 4 || info->bootmode == 1
|
|
|| info->bootmode == 8 || info->bootmode == 9) {
|
|
info->sc.sc_ibat = -1; /* not normal boot */
|
|
return;
|
|
}
|
|
info->sc.solution = info->sc.last_solution;
|
|
chr_debug("debug: %d, %d, %d\n", info->bootmode,
|
|
info->sc.disable_in_this_plug, info->sc.solution);
|
|
if (info->sc.disable_in_this_plug == false) {
|
|
chr_debug("sck: %d %d %d %d %d\n",
|
|
info->sc.pre_ibat,
|
|
info->sc.sc_ibat,
|
|
pdata->charging_current_limit,
|
|
pdata->thermal_charging_current_limit,
|
|
info->sc.solution);
|
|
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;
|
|
else
|
|
info->sc.sc_ibat = 500000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
info->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 &&
|
|
info->sc.disable_in_this_plug == false) {
|
|
pdata->charging_current_limit = info->sc.sc_ibat;
|
|
}
|
|
}
|
|
|
|
void sc_init(struct smartcharging *sc)
|
|
{
|
|
sc->enable = false;
|
|
sc->battery_size = 3000;
|
|
sc->start_time = 0;
|
|
sc->end_time = 80000;
|
|
sc->current_limit = 2000;
|
|
sc->target_percentage = 80;
|
|
sc->left_time_for_cv = 3600;
|
|
sc->pre_ibat = -1;
|
|
}
|
|
|
|
static ssize_t enable_sc_show(
|
|
struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
chr_err(
|
|
"[enable smartcharging] : %d\n",
|
|
info->sc.enable);
|
|
|
|
return sprintf(buf, "%d\n", info->sc.enable);
|
|
}
|
|
|
|
static ssize_t enable_sc_store(
|
|
struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
unsigned long val = 0;
|
|
int ret;
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
chr_err("[enable smartcharging] buf is %s\n", buf);
|
|
ret = kstrtoul(buf, 10, &val);
|
|
if (ret == -ERANGE || ret == -EINVAL)
|
|
return -EINVAL;
|
|
if (val == 0)
|
|
info->sc.enable = false;
|
|
else
|
|
info->sc.enable = true;
|
|
|
|
chr_err(
|
|
"[enable smartcharging]enable smartcharging=%d\n",
|
|
info->sc.enable);
|
|
}
|
|
return size;
|
|
}
|
|
static DEVICE_ATTR_RW(enable_sc);
|
|
|
|
static ssize_t sc_stime_show(
|
|
struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
chr_err(
|
|
"[smartcharging stime] : %d\n",
|
|
info->sc.start_time);
|
|
|
|
return sprintf(buf, "%d\n", info->sc.start_time);
|
|
}
|
|
|
|
static ssize_t sc_stime_store(
|
|
struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
long val = 0;
|
|
int ret;
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
chr_err("[smartcharging stime] buf is %s\n", buf);
|
|
ret = kstrtol(buf, 10, &val);
|
|
if (ret == -ERANGE || ret == -EINVAL)
|
|
return -EINVAL;
|
|
if (val < 0) {
|
|
chr_err(
|
|
"[smartcharging stime] val is %ld ??\n",
|
|
val);
|
|
val = 0;
|
|
}
|
|
|
|
if (val >= 0)
|
|
info->sc.start_time = (int)val;
|
|
|
|
chr_err(
|
|
"[smartcharging stime]enable smartcharging=%d\n",
|
|
info->sc.start_time);
|
|
}
|
|
return size;
|
|
}
|
|
static DEVICE_ATTR_RW(sc_stime);
|
|
|
|
static ssize_t sc_etime_show(
|
|
struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
chr_err(
|
|
"[smartcharging etime] : %d\n",
|
|
info->sc.end_time);
|
|
|
|
return sprintf(buf, "%d\n", info->sc.end_time);
|
|
}
|
|
|
|
static ssize_t sc_etime_store(
|
|
struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
long val = 0;
|
|
int ret;
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
chr_err("[smartcharging etime] buf is %s\n", buf);
|
|
ret = kstrtol(buf, 10, &val);
|
|
if (ret == -ERANGE || ret == -EINVAL)
|
|
return -EINVAL;
|
|
if (val < 0) {
|
|
chr_err(
|
|
"[smartcharging etime] val is %ld ??\n",
|
|
val);
|
|
val = 0;
|
|
}
|
|
|
|
if (val >= 0)
|
|
info->sc.end_time = (int)val;
|
|
|
|
chr_err(
|
|
"[smartcharging stime]enable smartcharging=%d\n",
|
|
info->sc.end_time);
|
|
}
|
|
return size;
|
|
}
|
|
static DEVICE_ATTR_RW(sc_etime);
|
|
|
|
static ssize_t sc_tuisoc_show(
|
|
struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
chr_err(
|
|
"[smartcharging target uisoc] : %d\n",
|
|
info->sc.target_percentage);
|
|
|
|
return sprintf(buf, "%d\n", info->sc.target_percentage);
|
|
}
|
|
|
|
static ssize_t sc_tuisoc_store(
|
|
struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
long val = 0;
|
|
int ret;
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
chr_err("[smartcharging tuisoc] buf is %s\n", buf);
|
|
ret = kstrtol(buf, 10, &val);
|
|
if (ret == -ERANGE || ret == -EINVAL)
|
|
return -EINVAL;
|
|
if (val < 0) {
|
|
chr_err(
|
|
"[smartcharging tuisoc] val is %ld ??\n",
|
|
val);
|
|
val = 0;
|
|
}
|
|
|
|
if (val >= 0)
|
|
info->sc.target_percentage = (int)val;
|
|
|
|
chr_err(
|
|
"[smartcharging stime]tuisoc=%d\n",
|
|
info->sc.target_percentage);
|
|
}
|
|
return size;
|
|
}
|
|
static DEVICE_ATTR_RW(sc_tuisoc);
|
|
|
|
static ssize_t sc_ibat_limit_show(
|
|
struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
chr_err(
|
|
"[smartcharging ibat limit] : %d\n",
|
|
info->sc.current_limit);
|
|
|
|
return sprintf(buf, "%d\n", info->sc.current_limit);
|
|
}
|
|
|
|
static ssize_t sc_ibat_limit_store(
|
|
struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
long val = 0;
|
|
int ret;
|
|
struct power_supply *chg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
|
|
chg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (chg_psy == NULL || IS_ERR(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy);
|
|
if (info == NULL)
|
|
return -EINVAL;
|
|
|
|
if (buf != NULL && size != 0) {
|
|
chr_err("[smartcharging ibat limit] buf is %s\n", buf);
|
|
ret = kstrtol(buf, 10, &val);
|
|
if (ret == -ERANGE || ret == -EINVAL)
|
|
return -EINVAL;
|
|
if (val < 0) {
|
|
chr_err(
|
|
"[smartcharging ibat limit] val is %ld ??\n",
|
|
(int)val);
|
|
val = 0;
|
|
}
|
|
|
|
if (val >= 0)
|
|
info->sc.current_limit = (int)val;
|
|
|
|
chr_err(
|
|
"[smartcharging ibat limit]=%d\n",
|
|
info->sc.current_limit);
|
|
}
|
|
return size;
|
|
}
|
|
static DEVICE_ATTR_RW(sc_ibat_limit);
|
|
|
|
int mtk_chg_enable_vbus_ovp(bool enable)
|
|
{
|
|
static struct mtk_charger *pinfo;
|
|
int ret = 0;
|
|
u32 sw_ovp = 0;
|
|
struct power_supply *psy;
|
|
|
|
if (pinfo == NULL) {
|
|
psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (psy == NULL) {
|
|
chr_err("[%s]psy is not rdy\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
pinfo = (struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (pinfo == NULL) {
|
|
chr_err("[%s]mtk_gauge is not rdy\n", __func__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (enable)
|
|
sw_ovp = pinfo->data.max_charger_voltage_setting;
|
|
else
|
|
sw_ovp = pinfo->data.vbus_sw_ovp_voltage;
|
|
|
|
/* Enable/Disable SW OVP status */
|
|
pinfo->data.max_charger_voltage = sw_ovp;
|
|
|
|
disable_hw_ovp(pinfo, enable);
|
|
|
|
chr_err("[%s] en:%d ovp:%d\n",
|
|
__func__, enable, sw_ovp);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(mtk_chg_enable_vbus_ovp);
|
|
|
|
/* return false if vbus is over max_charger_voltage */
|
|
static bool mtk_chg_check_vbus(struct mtk_charger *info)
|
|
{
|
|
int vchr = 0;
|
|
|
|
vchr = get_vbus(info) * 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 mtk_charger *info)
|
|
{
|
|
#if defined(BATTERY_NOTIFY_CASE_0001_VCHARGER)
|
|
int vchr = 0;
|
|
|
|
vchr = get_vbus(info) * 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 mtk_charger *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 mtk_charger *info)
|
|
{
|
|
switch (info->notify_test_mode) {
|
|
case 1:
|
|
info->notify_code = CHG_VBUS_OV_STATUS;
|
|
chr_debug("[%s] CASE_0001_VCHARGER\n", __func__);
|
|
break;
|
|
case 2:
|
|
info->notify_code = CHG_BAT_OT_STATUS;
|
|
chr_debug("[%s] CASE_0002_VBATTEMP\n", __func__);
|
|
break;
|
|
case 3:
|
|
info->notify_code = CHG_OC_STATUS;
|
|
chr_debug("[%s] CASE_0003_ICHARGING\n", __func__);
|
|
break;
|
|
case 4:
|
|
info->notify_code = CHG_BAT_OV_STATUS;
|
|
chr_debug("[%s] CASE_0004_VBAT\n", __func__);
|
|
break;
|
|
case 5:
|
|
info->notify_code = CHG_ST_TMO_STATUS;
|
|
chr_debug("[%s] CASE_0005_TOTAL_CHARGINGTIME\n", __func__);
|
|
break;
|
|
case 6:
|
|
info->notify_code = CHG_BAT_LT_STATUS;
|
|
chr_debug("[%s] CASE6: VBATTEMP_LOW\n", __func__);
|
|
break;
|
|
case 7:
|
|
info->notify_code = CHG_TYPEC_WD_STATUS;
|
|
chr_debug("[%s] CASE7: Moisture Detection\n", __func__);
|
|
break;
|
|
default:
|
|
chr_debug("[%s] Unknown BN_TestMode Code: %x\n",
|
|
__func__, info->notify_test_mode);
|
|
}
|
|
mtk_chgstat_notify(info);
|
|
}
|
|
|
|
static void mtk_battery_notify_check(struct mtk_charger *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 mtk_chg_get_tchg(struct mtk_charger *info)
|
|
{
|
|
int ret;
|
|
int tchg_min = -127, tchg_max = -127;
|
|
struct charger_data *pdata;
|
|
bool en = false;
|
|
|
|
pdata = &info->chg_data[CHG1_SETTING];
|
|
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 (info->chg2_dev) {
|
|
pdata = &info->chg_data[CHG2_SETTING];
|
|
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->chg_data[DVCHG1_SETTING];
|
|
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->chg_data[DVCHG2_SETTING];
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->hvdvchg1_dev) {
|
|
pdata = &info->chg_data[HVDVCHG1_SETTING];
|
|
pdata->junction_temp_min = -127;
|
|
pdata->junction_temp_max = -127;
|
|
ret = charger_dev_is_enabled(info->hvdvchg1_dev, &en);
|
|
if (ret >= 0 && en) {
|
|
ret = charger_dev_get_adc(info->hvdvchg1_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->hvdvchg2_dev) {
|
|
pdata = &info->chg_data[HVDVCHG2_SETTING];
|
|
pdata->junction_temp_min = -127;
|
|
pdata->junction_temp_max = -127;
|
|
ret = charger_dev_is_enabled(info->hvdvchg2_dev, &en);
|
|
if (ret >= 0 && en) {
|
|
ret = charger_dev_get_adc(info->hvdvchg2_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 mtk_charger *info)
|
|
{
|
|
bool charging = true;
|
|
bool chg_dev_chgen = true;
|
|
int temperature;
|
|
struct battery_thermal_protection_data *thermal;
|
|
int uisoc = 0;
|
|
|
|
if (get_charger_type(info) == POWER_SUPPLY_TYPE_UNKNOWN)
|
|
return;
|
|
|
|
temperature = info->battery_temp;
|
|
thermal = &info->thermal;
|
|
uisoc = get_uisoc(info);
|
|
|
|
info->setting.vbat_mon_en = true;
|
|
if (info->enable_sw_jeita == true || info->enable_vbat_mon != true ||
|
|
info->batpro_done == true)
|
|
info->setting.vbat_mon_en = false;
|
|
|
|
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);
|
|
|
|
if (charging && uisoc < 80 && info->batpro_done == true) {
|
|
info->setting.vbat_mon_en = true;
|
|
info->batpro_done = false;
|
|
info->stop_6pin_re_en = false;
|
|
}
|
|
|
|
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 saf_cmd:%d bat_mon:%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, info->safety_timer_cmd,
|
|
info->enable_vbat_mon, info->batpro_done);
|
|
|
|
charger_dev_is_enabled(info->chg1_dev, &chg_dev_chgen);
|
|
|
|
if (charging != info->can_charging)
|
|
_mtk_enable_charging(info, charging);
|
|
else if (charging == false && chg_dev_chgen == true)
|
|
_mtk_enable_charging(info, charging);
|
|
|
|
info->can_charging = charging;
|
|
}
|
|
|
|
static bool charger_init_algo(struct mtk_charger *info)
|
|
{
|
|
struct chg_alg_device *alg;
|
|
int idx = 0;
|
|
|
|
info->chg1_dev = get_charger_by_name("primary_chg");
|
|
if (info->chg1_dev)
|
|
chr_err("%s, Found primary charger\n", __func__);
|
|
else {
|
|
chr_err("%s, *** Error : can't find primary charger ***\n"
|
|
, __func__);
|
|
return false;
|
|
}
|
|
|
|
alg = get_chg_alg_by_name("pe5p");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe5p fail\n");
|
|
else {
|
|
chr_err("get pe5p success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE5P_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("hvbp");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get hvbp fail\n");
|
|
else {
|
|
chr_err("get hvbp success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = HVBP_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pe5");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe5 fail\n");
|
|
else {
|
|
chr_err("get pe5 success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE5_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pe45");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe45 fail\n");
|
|
else {
|
|
chr_err("get pe45 success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE4_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pe4");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe4 fail\n");
|
|
else {
|
|
chr_err("get pe4 success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE4_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pd");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pd fail\n");
|
|
else {
|
|
chr_err("get pd success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PDC_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pe2");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe2 fail\n");
|
|
else {
|
|
chr_err("get pe2 success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE2_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
idx++;
|
|
|
|
alg = get_chg_alg_by_name("pe");
|
|
info->alg[idx] = alg;
|
|
if (alg == NULL)
|
|
chr_err("get pe fail\n");
|
|
else {
|
|
chr_err("get pe success\n");
|
|
alg->config = info->config;
|
|
alg->alg_id = PE_ID;
|
|
chg_alg_init_algo(alg);
|
|
register_chg_alg_notifier(alg, &info->chg_alg_nb);
|
|
}
|
|
|
|
chr_err("config is %d\n", info->config);
|
|
if (info->config == DUAL_CHARGERS_IN_SERIES) {
|
|
info->chg2_dev = get_charger_by_name("secondary_chg");
|
|
if (info->chg2_dev)
|
|
chr_err("Found secondary charger\n");
|
|
else {
|
|
chr_err("*** Error : can't find secondary charger ***\n");
|
|
return false;
|
|
}
|
|
} else if (info->config == DIVIDER_CHARGER ||
|
|
info->config == DUAL_DIVIDER_CHARGERS) {
|
|
info->dvchg1_dev = get_charger_by_name("primary_dvchg");
|
|
if (info->dvchg1_dev)
|
|
chr_err("Found primary divider charger\n");
|
|
else {
|
|
chr_err("*** Error : can't find primary divider charger ***\n");
|
|
return false;
|
|
}
|
|
if (info->config == DUAL_DIVIDER_CHARGERS) {
|
|
info->dvchg2_dev =
|
|
get_charger_by_name("secondary_dvchg");
|
|
if (info->dvchg2_dev)
|
|
chr_err("Found secondary divider charger\n");
|
|
else {
|
|
chr_err("*** Error : can't find secondary divider charger ***\n");
|
|
return false;
|
|
}
|
|
}
|
|
} else if (info->config == HVDIVIDER_CHARGER ||
|
|
info->config == DUAL_HVDIVIDER_CHARGERS) {
|
|
info->hvdvchg1_dev = get_charger_by_name("hvdiv2_chg1");
|
|
if (info->hvdvchg1_dev)
|
|
chr_err("Found primary hvdivider charger\n");
|
|
else {
|
|
chr_err("*** Error : can't find primary hvdivider charger ***\n");
|
|
return false;
|
|
}
|
|
if (info->config == DUAL_HVDIVIDER_CHARGERS) {
|
|
info->hvdvchg2_dev = get_charger_by_name("hvdiv2_chg2");
|
|
if (info->hvdvchg2_dev)
|
|
chr_err("Found secondary hvdivider charger\n");
|
|
else {
|
|
chr_err("*** Error : can't find secondary hvdivider charger ***\n");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
chr_err("register chg1 notifier %d %d\n",
|
|
info->chg1_dev != NULL, info->algo.do_event != NULL);
|
|
if (info->chg1_dev != NULL && info->algo.do_event != NULL) {
|
|
chr_err("register chg1 notifier done\n");
|
|
info->chg1_nb.notifier_call = info->algo.do_event;
|
|
register_charger_device_notifier(info->chg1_dev,
|
|
&info->chg1_nb);
|
|
charger_dev_set_drvdata(info->chg1_dev, info);
|
|
}
|
|
|
|
chr_err("register dvchg chg1 notifier %d %d\n",
|
|
info->dvchg1_dev != NULL, info->algo.do_dvchg1_event != NULL);
|
|
if (info->dvchg1_dev != NULL && info->algo.do_dvchg1_event != NULL) {
|
|
chr_err("register dvchg chg1 notifier done\n");
|
|
info->dvchg1_nb.notifier_call = info->algo.do_dvchg1_event;
|
|
register_charger_device_notifier(info->dvchg1_dev,
|
|
&info->dvchg1_nb);
|
|
charger_dev_set_drvdata(info->dvchg1_dev, info);
|
|
}
|
|
|
|
chr_err("register dvchg chg2 notifier %d %d\n",
|
|
info->dvchg2_dev != NULL, info->algo.do_dvchg2_event != NULL);
|
|
if (info->dvchg2_dev != NULL && info->algo.do_dvchg2_event != NULL) {
|
|
chr_err("register dvchg chg2 notifier done\n");
|
|
info->dvchg2_nb.notifier_call = info->algo.do_dvchg2_event;
|
|
register_charger_device_notifier(info->dvchg2_dev,
|
|
&info->dvchg2_nb);
|
|
charger_dev_set_drvdata(info->dvchg2_dev, info);
|
|
}
|
|
|
|
chr_err("register hvdvchg chg1 notifier %d %d\n",
|
|
info->hvdvchg1_dev != NULL,
|
|
info->algo.do_hvdvchg1_event != NULL);
|
|
if (info->hvdvchg1_dev != NULL &&
|
|
info->algo.do_hvdvchg1_event != NULL) {
|
|
chr_err("register hvdvchg chg1 notifier done\n");
|
|
info->hvdvchg1_nb.notifier_call = info->algo.do_hvdvchg1_event;
|
|
register_charger_device_notifier(info->hvdvchg1_dev,
|
|
&info->hvdvchg1_nb);
|
|
charger_dev_set_drvdata(info->hvdvchg1_dev, info);
|
|
}
|
|
|
|
chr_err("register hvdvchg chg2 notifier %d %d\n",
|
|
info->hvdvchg2_dev != NULL,
|
|
info->algo.do_hvdvchg2_event != NULL);
|
|
if (info->hvdvchg2_dev != NULL &&
|
|
info->algo.do_hvdvchg2_event != NULL) {
|
|
chr_err("register hvdvchg chg2 notifier done\n");
|
|
info->hvdvchg2_nb.notifier_call = info->algo.do_hvdvchg2_event;
|
|
register_charger_device_notifier(info->hvdvchg2_dev,
|
|
&info->hvdvchg2_nb);
|
|
charger_dev_set_drvdata(info->hvdvchg2_dev, info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int mtk_charger_force_disable_power_path(struct mtk_charger *info,
|
|
int idx, bool disable);
|
|
static int mtk_charger_plug_out(struct mtk_charger *info)
|
|
{
|
|
struct charger_data *pdata1 = &info->chg_data[CHG1_SETTING];
|
|
struct charger_data *pdata2 = &info->chg_data[CHG2_SETTING];
|
|
struct chg_alg_device *alg;
|
|
struct chg_alg_notify notify;
|
|
int i;
|
|
|
|
chr_err("%s\n", __func__);
|
|
info->chr_type = POWER_SUPPLY_TYPE_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;
|
|
|
|
notify.evt = EVT_PLUG_OUT;
|
|
notify.value = 0;
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
chg_alg_notifier_call(alg, ¬ify);
|
|
chg_alg_plugout_reset(alg);
|
|
}
|
|
memset(&info->sc.data, 0, sizeof(struct scd_cmd_param_t_1));
|
|
charger_dev_set_input_current(info->chg1_dev, 100000);
|
|
charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage);
|
|
charger_dev_plug_out(info->chg1_dev);
|
|
mtk_charger_force_disable_power_path(info, CHG1_SETTING, true);
|
|
|
|
if (info->enable_vbat_mon)
|
|
charger_dev_enable_6pin_battery_charging(info->chg1_dev, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_charger_plug_in(struct mtk_charger *info,
|
|
int chr_type)
|
|
{
|
|
struct chg_alg_device *alg;
|
|
struct chg_alg_notify notify;
|
|
int i, vbat;
|
|
|
|
chr_debug("%s\n",
|
|
__func__);
|
|
|
|
info->chr_type = chr_type;
|
|
info->usb_type = get_usb_type(info);
|
|
info->charger_thread_polling = true;
|
|
|
|
info->can_charging = true;
|
|
//info->enable_dynamic_cv = true;
|
|
info->safety_timeout = false;
|
|
info->vbusov_stat = false;
|
|
info->old_cv = 0;
|
|
info->stop_6pin_re_en = false;
|
|
info->batpro_done = false;
|
|
smart_charging(info);
|
|
chr_err("mtk_is_charger_on plug in, type:%d\n", chr_type);
|
|
|
|
vbat = get_battery_voltage(info);
|
|
|
|
notify.evt = EVT_PLUG_IN;
|
|
notify.value = 0;
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
chg_alg_notifier_call(alg, ¬ify);
|
|
chg_alg_set_prop(alg, ALG_REF_VBAT, vbat);
|
|
}
|
|
|
|
memset(&info->sc.data, 0, sizeof(struct scd_cmd_param_t_1));
|
|
info->sc.disable_in_this_plug = false;
|
|
|
|
charger_dev_plug_in(info->chg1_dev);
|
|
mtk_charger_force_disable_power_path(info, CHG1_SETTING, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool mtk_is_charger_on(struct mtk_charger *info)
|
|
{
|
|
int chr_type;
|
|
|
|
chr_type = get_charger_type(info);
|
|
if (chr_type == POWER_SUPPLY_TYPE_UNKNOWN) {
|
|
if (info->chr_type != POWER_SUPPLY_TYPE_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 == POWER_SUPPLY_TYPE_UNKNOWN)
|
|
mtk_charger_plug_in(info, chr_type);
|
|
else {
|
|
info->chr_type = chr_type;
|
|
info->usb_type = get_usb_type(info);
|
|
}
|
|
|
|
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 = 0;
|
|
mutex_unlock(&info->cable_out_lock);
|
|
}
|
|
}
|
|
|
|
if (chr_type == POWER_SUPPLY_TYPE_UNKNOWN)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void charger_send_kpoc_uevent(struct mtk_charger *info)
|
|
{
|
|
static bool first_time = true;
|
|
ktime_t ktime_now;
|
|
|
|
if (first_time) {
|
|
info->uevent_time_check = ktime_get();
|
|
first_time = false;
|
|
} else {
|
|
ktime_now = ktime_get();
|
|
if ((ktime_ms_delta(ktime_now, info->uevent_time_check) / 1000) >= 60) {
|
|
mtk_chgstat_notify(info);
|
|
info->uevent_time_check = ktime_now;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpoc_power_off_check(struct mtk_charger *info)
|
|
{
|
|
unsigned int boot_mode = info->bootmode;
|
|
int vbus = 0;
|
|
int counter = 0;
|
|
/* 8 = KERNEL_POWER_OFF_CHARGING_BOOT */
|
|
/* 9 = LOW_POWER_OFF_CHARGING_BOOT */
|
|
if (boot_mode == 8 || boot_mode == 9) {
|
|
vbus = get_vbus(info);
|
|
if (vbus >= 0 && vbus < 2500 && !mtk_is_charger_on(info) && !info->pd_reset) {
|
|
chr_err("Unplug Charger/USB in KPOC mode, vbus=%d, shutdown\n", vbus);
|
|
while (1) {
|
|
if (counter >= 20000) {
|
|
chr_err("%s, wait too long\n", __func__);
|
|
kernel_power_off();
|
|
break;
|
|
}
|
|
if (info->is_suspend == false) {
|
|
chr_err("%s, not in suspend, shutdown\n", __func__);
|
|
kernel_power_off();
|
|
break;
|
|
} else {
|
|
chr_err("%s, suspend! cannot shutdown\n", __func__);
|
|
msleep(20);
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
charger_send_kpoc_uevent(info);
|
|
}
|
|
}
|
|
|
|
static void charger_status_check(struct mtk_charger *info)
|
|
{
|
|
union power_supply_propval online, status;
|
|
struct power_supply *chg_psy = NULL;
|
|
int ret;
|
|
bool charging = true;
|
|
|
|
chg_psy = power_supply_get_by_name("primary_chg");
|
|
if (IS_ERR_OR_NULL(chg_psy)) {
|
|
chr_err("%s Couldn't get chg_psy\n", __func__);
|
|
} else {
|
|
ret = power_supply_get_property(chg_psy,
|
|
POWER_SUPPLY_PROP_ONLINE, &online);
|
|
|
|
ret = power_supply_get_property(chg_psy,
|
|
POWER_SUPPLY_PROP_STATUS, &status);
|
|
|
|
if (!online.intval)
|
|
charging = false;
|
|
else {
|
|
if (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
|
|
charging = false;
|
|
}
|
|
}
|
|
if (charging != info->is_charging)
|
|
power_supply_changed(info->psy1);
|
|
info->is_charging = charging;
|
|
}
|
|
|
|
|
|
static char *dump_charger_type(int chg_type, int usb_type)
|
|
{
|
|
switch (chg_type) {
|
|
case POWER_SUPPLY_TYPE_UNKNOWN:
|
|
return "none";
|
|
case POWER_SUPPLY_TYPE_USB:
|
|
if (usb_type == POWER_SUPPLY_USB_TYPE_SDP)
|
|
return "usb";
|
|
else
|
|
return "nonstd";
|
|
case POWER_SUPPLY_TYPE_USB_CDP:
|
|
return "usb-h";
|
|
case POWER_SUPPLY_TYPE_USB_DCP:
|
|
return "std";
|
|
//case POWER_SUPPLY_TYPE_USB_FLOAT:
|
|
// return "nonstd";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static int charger_routine_thread(void *arg)
|
|
{
|
|
struct mtk_charger *info = arg;
|
|
unsigned long flags;
|
|
unsigned int init_times = 3;
|
|
static bool is_module_init_done;
|
|
bool is_charger_on;
|
|
int ret;
|
|
int vbat_min, vbat_max;
|
|
u32 chg_cv = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
while (is_module_init_done == false) {
|
|
if (charger_init_algo(info) == true) {
|
|
is_module_init_done = true;
|
|
if (info->charger_unlimited) {
|
|
info->enable_sw_safety_timer = false;
|
|
charger_dev_enable_safety_timer(info->chg1_dev, false);
|
|
}
|
|
}
|
|
else {
|
|
if (init_times > 0) {
|
|
chr_err("retry to init charger\n");
|
|
init_times = init_times - 1;
|
|
msleep(10000);
|
|
} else {
|
|
chr_err("holding to init charger\n");
|
|
msleep(60000);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
info->battery_temp = get_battery_temperature(info);
|
|
ret = charger_dev_get_adc(info->chg1_dev,
|
|
ADC_CHANNEL_VBAT, &vbat_min, &vbat_max);
|
|
ret = charger_dev_get_constant_voltage(info->chg1_dev, &chg_cv);
|
|
|
|
if (vbat_min != 0)
|
|
vbat_min = vbat_min / 1000;
|
|
|
|
chr_err("Vbat=%d vbats=%d vbus:%d ibus:%d I=%d T=%d uisoc:%d type:%s>%s pd:%d swchg_ibat:%d cv:%d\n",
|
|
get_battery_voltage(info),
|
|
vbat_min,
|
|
get_vbus(info),
|
|
get_ibus(info),
|
|
get_battery_current(info),
|
|
info->battery_temp,
|
|
get_uisoc(info),
|
|
dump_charger_type(info->chr_type, info->usb_type),
|
|
dump_charger_type(get_charger_type(info), get_usb_type(info)),
|
|
info->pd_type, get_ibat(info), chg_cv);
|
|
|
|
is_charger_on = mtk_is_charger_on(info);
|
|
|
|
if (info->charger_thread_polling == true)
|
|
mtk_charger_start_timer(info);
|
|
|
|
check_battery_exist(info);
|
|
check_dynamic_mivr(info);
|
|
charger_check_status(info);
|
|
kpoc_power_off_check(info);
|
|
|
|
if (is_disable_charger(info) == false &&
|
|
is_charger_on == true &&
|
|
info->can_charging == true) {
|
|
if (info->algo.do_algorithm)
|
|
info->algo.do_algorithm(info);
|
|
charger_status_check(info);
|
|
} else {
|
|
chr_debug("disable charging %d %d %d\n",
|
|
is_disable_charger(info), is_charger_on, info->can_charging);
|
|
}
|
|
if (info->bootmode != 1 && info->bootmode != 2 && info->bootmode != 4
|
|
&& info->bootmode != 8 && info->bootmode != 9)
|
|
smart_charging(info);
|
|
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;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
static int charger_pm_event(struct notifier_block *notifier,
|
|
unsigned long pm_event, void *unused)
|
|
{
|
|
ktime_t ktime_now;
|
|
struct timespec64 now;
|
|
struct mtk_charger *info;
|
|
|
|
info = container_of(notifier,
|
|
struct mtk_charger, pm_notifier);
|
|
|
|
switch (pm_event) {
|
|
case PM_SUSPEND_PREPARE:
|
|
info->is_suspend = true;
|
|
chr_debug("%s: enter PM_SUSPEND_PREPARE\n", __func__);
|
|
break;
|
|
case PM_POST_SUSPEND:
|
|
info->is_suspend = false;
|
|
chr_debug("%s: enter PM_POST_SUSPEND\n", __func__);
|
|
ktime_now = ktime_get_boottime();
|
|
now = ktime_to_timespec64(ktime_now);
|
|
|
|
if (timespec64_compare(&now, &info->endtime) >= 0 &&
|
|
info->endtime.tv_sec != 0 &&
|
|
info->endtime.tv_nsec != 0) {
|
|
chr_err("%s: alarm timeout, wake up charger\n",
|
|
__func__);
|
|
__pm_relax(info->charger_wakelock);
|
|
info->endtime.tv_sec = 0;
|
|
info->endtime.tv_nsec = 0;
|
|
_wake_up_charger(info);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
static enum alarmtimer_restart
|
|
mtk_charger_alarm_timer_func(struct alarm *alarm, ktime_t now)
|
|
{
|
|
struct mtk_charger *info =
|
|
container_of(alarm, struct mtk_charger, charger_timer);
|
|
|
|
if (info->is_suspend == false) {
|
|
_wake_up_charger(info);
|
|
} else {
|
|
__pm_stay_awake(info->charger_wakelock);
|
|
}
|
|
|
|
return ALARMTIMER_NORESTART;
|
|
}
|
|
|
|
static void mtk_charger_init_timer(struct mtk_charger *info)
|
|
{
|
|
alarm_init(&info->charger_timer, ALARM_BOOTTIME,
|
|
mtk_charger_alarm_timer_func);
|
|
mtk_charger_start_timer(info);
|
|
|
|
}
|
|
|
|
/* prize liuyong, add charging limit node 20230605 start */
|
|
static ssize_t show_cmd_charge_disable(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *info = dev->driver_data;
|
|
|
|
pr_info("[charge] %s : %d\n",__func__, info->cmd_discharging);
|
|
return sprintf(buf, "%d\n",info->cmd_discharging);
|
|
}
|
|
|
|
static ssize_t store_cmd_charge_disable(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct mtk_charger *pinfo = dev->driver_data;
|
|
unsigned int reg = 0;
|
|
int ret = 0;
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start
|
|
bool dvchg1_chip_enabled = false;
|
|
//struct chg_alg_device *alg;
|
|
|
|
charger_dev_is_enabled(pinfo->dvchg1_dev, &dvchg1_chip_enabled);
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end
|
|
|
|
pr_info("[charge] %s\n", __func__);
|
|
if (buf != NULL && size != 0) {
|
|
pr_info("[store_cmd_charge_disable] buf is %s and size is %zu\n", buf, size);
|
|
ret = kstrtouint(buf, 16, ®);
|
|
if(reg == 1){
|
|
pinfo->cmd_discharging = true;
|
|
#if IS_ENABLED(CONFIG_PRIZE_MT5728_SUPPORT_30W)
|
|
/* Turn wireless charge off if support */
|
|
turn_off_5728(1);
|
|
//turn_off_5725(1);
|
|
//set_wireless_disable_flag(true);
|
|
#endif /*CONFIG_PRIZE_MT5725_SUPPORT_15W*/
|
|
}else if(reg == 0){
|
|
#if IS_ENABLED(CONFIG_PRIZE_MT5728_SUPPORT_30W)
|
|
/* Resume wireless charge on if support */
|
|
turn_off_5728(0);
|
|
//set_wireless_disable_flag(false);
|
|
//turn_off_5725(0);
|
|
#endif /*CONFIG_PRIZE_MT5725_SUPPORT_15W*/
|
|
pinfo->cmd_discharging = false;
|
|
}else{
|
|
pr_info("[store_cmd_charge_disable] input err please 0 or 1\n");
|
|
}
|
|
|
|
if((pinfo->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 1)){
|
|
charger_dev_enable(pinfo->chg1_dev, false);
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start
|
|
if(dvchg1_chip_enabled){
|
|
charger_dev_enable(pinfo->dvchg1_dev, false);
|
|
// charger_dev_enable_chip(pinfo->chg2_dev, false);
|
|
}
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end
|
|
charger_dev_do_event(pinfo->chg1_dev,EVENT_DISCHARGE, 0);
|
|
pr_info("[store_cmd_charge_disable] disable charge\n");
|
|
}else if((pinfo->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 0)){
|
|
/*
|
|
for (i = 0; i < MAX_ALG_NO; i++)
|
|
{
|
|
alg = pinfo->alg[i];
|
|
chg_alg_plugout_reset(alg);
|
|
}
|
|
*/
|
|
charger_dev_enable(pinfo->chg1_dev, true);
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start
|
|
charger_dev_enable(pinfo->dvchg1_dev, true);
|
|
//mtk_pe50_set_is_enable(pinfo, true);
|
|
//FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end
|
|
charger_dev_do_event(pinfo->chg1_dev,EVENT_RECHARGE, 0);
|
|
pr_info("[store_cmd_charge_disable] enable charge \n");
|
|
}else {
|
|
pr_info("[store_cmd_charge_disable] No USB connection \n");
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(cmd_charge_disable, 0664, show_cmd_charge_disable,
|
|
store_cmd_charge_disable);
|
|
/* prize liuyong, add charging limit node 20230605 end */
|
|
static int mtk_charger_setup_files(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct proc_dir_entry *battery_dir = NULL, *entry = NULL;
|
|
struct mtk_charger *info = platform_get_drvdata(pdev);
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sw_jeita);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sw_ovp_threshold);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_chr_type);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_enable_meta_current_limit);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_fast_chg_indicator);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_Charging_mode);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_pd_type);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_High_voltage_chg_enable);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_Rust_detect);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_Thermal_throttle);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_alg_new_arbitration);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_alg_unchangeable);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_vbat_mon);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_Pump_Express);
|
|
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_ADC_Charging_Current);
|
|
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_charger_log_level);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
/* Battery warning */
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_BatteryNotify);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
/* sysfs node */
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_enable_sc);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sc_stime);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sc_etime);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sc_tuisoc);
|
|
if (ret)
|
|
goto _out;
|
|
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_sc_ibat_limit);
|
|
if (ret)
|
|
goto _out;
|
|
/* prize liuyong, add charging limit node 20230605 start */
|
|
ret = device_create_file(&(pdev->dev), &dev_attr_cmd_charge_disable);
|
|
if (ret)
|
|
goto _out;
|
|
/* prize liuyong, add charging limit node 20230605 end */
|
|
|
|
battery_dir = proc_mkdir("mtk_battery_cmd", NULL);
|
|
if (!battery_dir) {
|
|
chr_err("%s: mkdir /proc/mtk_battery_cmd failed\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
entry = proc_create_data("current_cmd", 0644, battery_dir,
|
|
&mtk_chg_current_cmd_fops, info);
|
|
if (!entry) {
|
|
ret = -ENODEV;
|
|
goto fail_procfs;
|
|
}
|
|
entry = proc_create_data("en_power_path", 0644, battery_dir,
|
|
&mtk_chg_en_power_path_fops, info);
|
|
if (!entry) {
|
|
ret = -ENODEV;
|
|
goto fail_procfs;
|
|
}
|
|
entry = proc_create_data("en_safety_timer", 0644, battery_dir,
|
|
&mtk_chg_en_safety_timer_fops, info);
|
|
if (!entry) {
|
|
ret = -ENODEV;
|
|
goto fail_procfs;
|
|
}
|
|
entry = proc_create_data("set_cv", 0644, battery_dir,
|
|
&mtk_chg_set_cv_fops, info);
|
|
if (!entry) {
|
|
ret = -ENODEV;
|
|
goto fail_procfs;
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail_procfs:
|
|
remove_proc_subtree("mtk_battery_cmd", NULL);
|
|
_out:
|
|
return ret;
|
|
}
|
|
|
|
// drv add tankaikun, add facoryt charger class, 20231204 start
|
|
#if IS_ENABLED(CONFIG_FACTORY_CHARGE)
|
|
static int mtk_map_fast_chrg_type(int alg_id) {
|
|
int i;
|
|
struct mtk_fast_chg_type_map fast_chg_type_maps[] = {
|
|
{MTK_FAST_CHARGER_TYPE_UNKNOWN, 0},
|
|
{MTK_FAST_CHARGER_TYPE_PEP, PE_ID},
|
|
{MTK_FAST_CHARGER_TYPE_PE20, PE2_ID},
|
|
{MTK_FAST_CHARGER_TYPE_PDC, PDC_ID},
|
|
{MTK_FAST_CHARGER_TYPE_PE40, PE4_ID},
|
|
{MTK_FAST_CHARGER_TYPE_PE50, PE5_ID},
|
|
{MTK_FAST_CHARGER_TYPE_HVBP, HVBP_ID},
|
|
{MTK_FAST_CHARGER_TYPE_PE5P, PE5P_ID},
|
|
{MTK_FAST_CHARGER_TYPE_WIRELESS_FAST, WL_ID},
|
|
};
|
|
|
|
for (i = 0; i<MTK_FAST_CHARGER_TYPE_MAX; i++) {
|
|
if (fast_chg_type_maps[i].fast_chrg_id == alg_id)
|
|
return fast_chg_type_maps[i].fast_chg_type;
|
|
}
|
|
|
|
return MTK_FAST_CHARGER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
int is_chg2_exist = 0;
|
|
EXPORT_SYMBOL_GPL(is_chg2_exist);
|
|
#if IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT)
|
|
static ssize_t chg2_exist_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
struct charger_device *chg_dev = get_charger_by_name("primary_dvchg");
|
|
if(chg_dev){
|
|
is_chg2_exist = 1;
|
|
}
|
|
return sprintf(buf, "%u\n", is_chg2_exist);
|
|
}
|
|
#endif /* CONFIG_SECOND_CHARGER_SUPPORT */
|
|
|
|
static ssize_t charger_type_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
struct power_supply *chrg_psy = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
int chrg_type = MTK_FAST_CHARGER_TYPE_UNKNOWN;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("[factroy_charge]get chrg_psy err\n");
|
|
return sprintf(buf, "%s\n", mtk_chg_type_name_list[chrg_type]);
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
|
|
pr_err("get_charger_type: %d\n", get_charger_type(info));
|
|
|
|
switch (get_charger_type(info)) {
|
|
case POWER_SUPPLY_TYPE_UNKNOWN:
|
|
chrg_type = MTK_CHARGER_TYPE_UNKNOWN;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB:
|
|
chrg_type = MTK_CHARGER_TYPE_SDP;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB_CDP:
|
|
chrg_type = MTK_CHARGER_TYPE_CDP;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB_DCP:
|
|
chrg_type = MTK_CHARGER_TYPE_DCP;
|
|
break;
|
|
default:
|
|
chrg_type = MTK_CHARGER_TYPE_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return sprintf(buf, "%s\n", mtk_chg_type_name_list[chrg_type]);
|
|
}
|
|
|
|
static ssize_t fast_charger_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
bool is_detected = false;
|
|
int i, ret;
|
|
struct chg_alg_device *alg = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto out;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info){
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto out;
|
|
}
|
|
|
|
if (info->pd_type == MTK_PD_CONNECT_PE_READY_SNK_APDO) {
|
|
is_detected = true;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if(alg->alg_id == PE5_ID) {
|
|
if (ret == ALG_RUNNING && alg->pe_ready_check_done){
|
|
is_detected = true;
|
|
chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected);
|
|
break;
|
|
}
|
|
} else {
|
|
if (ret == ALG_RUNNING) {
|
|
is_detected = true;
|
|
chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
return sprintf(buf, "%d\n", is_detected);
|
|
}
|
|
|
|
static ssize_t charger_algo_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
int i, ret, alg_id=0;
|
|
struct chg_alg_device *alg = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if(alg->alg_id == PE5_ID) {
|
|
if (ret == ALG_RUNNING && alg->pe_ready_check_done){
|
|
alg_id = alg->alg_id;
|
|
break;
|
|
}
|
|
} else {
|
|
if (ret == ALG_RUNNING) {
|
|
alg_id = alg->alg_id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
failed:
|
|
return sprintf(buf, "%s\n", mtk_fast_chg_algo_list[mtk_map_fast_chrg_type(alg_id)]);
|
|
}
|
|
|
|
static ssize_t fast_charger_enable_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
bool is_detected = false;
|
|
int i, ret;
|
|
struct chg_alg_device *alg = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if(alg->alg_id == PE5_ID) {
|
|
if (ret == ALG_RUNNING && alg->pe_ready_check_done){
|
|
is_detected = true;
|
|
chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected);
|
|
break;
|
|
}
|
|
} else {
|
|
if (ret == ALG_RUNNING) {
|
|
is_detected = true;
|
|
chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
failed:
|
|
return sprintf(buf, "%d\n", is_detected);
|
|
}
|
|
|
|
static ssize_t fast_charger_support_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
bool fast_charge_support = false;
|
|
int i;
|
|
struct chg_alg_device *alg = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
fast_charge_support = true;
|
|
}
|
|
|
|
failed:
|
|
return sprintf(buf, "%d\n",fast_charge_support);
|
|
}
|
|
|
|
static ssize_t fast_charger_power_show(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
int i, ret, pwr=10;
|
|
struct chg_alg_device *alg = NULL;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
alg = info->alg[i];
|
|
if (alg == NULL)
|
|
continue;
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if (ret == ALG_RUNNING && alg->pe_ready_check_done) {
|
|
chg_alg_get_prop(alg, ALG_CHARGE_PWR, &pwr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
failed:
|
|
return sprintf(buf, "%d\n", pwr);
|
|
}
|
|
|
|
#if 0
|
|
static ssize_t show_cmd_charge_disable(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL) {
|
|
pr_err("get bat_psy err\n");
|
|
return 0;
|
|
}
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
pr_info("[factroy_charge] %s : %d\n",__func__, info->cmd_discharging);
|
|
|
|
return sprintf(buf, "%d\n",info->cmd_discharging);
|
|
failed:
|
|
return sprintf(buf, "%d\n",0);
|
|
}
|
|
|
|
static ssize_t store_cmd_charge_disable(struct class *class, struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned int reg = 0;
|
|
int ret = 0,i = 0;
|
|
struct chg_alg_device *alg;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("get bat_psy err\n");
|
|
return 0;
|
|
}
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
pr_info("[factroy_charge] %s\n", __func__);
|
|
if (buf != NULL && count != 0) {
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] buf is %s and size is %zu\n", buf, count);
|
|
ret = kstrtouint(buf, 16, ®);
|
|
if(reg == 1) {
|
|
info->cmd_discharging = true;
|
|
} else if(reg == 0){
|
|
info->cmd_discharging = false;
|
|
} else{
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] input err please 0 or 1\n");
|
|
}
|
|
|
|
if((info->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 1)){
|
|
charger_dev_enable(info->chg1_dev, false);
|
|
charger_dev_do_event(info->chg1_dev,EVENT_DISCHARGE, 0);
|
|
charger_dev_enable_hz(info->chg1_dev, true);
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] disable charge\n");
|
|
} else if((info->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 0)){
|
|
for (i = 0; i < MAX_ALG_NO; i++)
|
|
{
|
|
alg = info->alg[i];
|
|
chg_alg_plugout_reset(alg);
|
|
}
|
|
charger_dev_enable_hz(info->chg1_dev, false);
|
|
charger_dev_enable(info->chg1_dev, true);
|
|
charger_dev_do_event(info->chg1_dev,EVENT_RECHARGE, 0);
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] enable charge \n");
|
|
} else {
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] No USB connection \n");
|
|
}
|
|
}
|
|
failed:
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
static ssize_t show_cmd_debug_temp(struct class *class, struct class_attribute *attr, char *buf)
|
|
{
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL) {
|
|
pr_err("get bat_psy err\n");
|
|
return 0;
|
|
}
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
pr_info("[factroy_charge] %s : %d:%d\n",__func__, info->debug_temp_en,info->debug_temp);
|
|
|
|
return sprintf(buf, "%d:%d\n",info->debug_temp_en, info->debug_temp);
|
|
failed:
|
|
return sprintf(buf, "%d:%d\n",0,0);
|
|
}
|
|
|
|
static ssize_t store_cmd_debug_temp(struct class *class, struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int reg = 0;
|
|
int ret = 0;
|
|
struct mtk_charger *info = NULL;
|
|
struct power_supply *chrg_psy = NULL;
|
|
|
|
chrg_psy = power_supply_get_by_name("mtk-master-charger");
|
|
if(chrg_psy == NULL){
|
|
pr_err("get bat_psy err\n");
|
|
return 0;
|
|
}
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy);
|
|
if(!info) {
|
|
pr_err("[factroy_charge] get chrg_psy err\n");
|
|
goto failed;
|
|
}
|
|
|
|
if (buf != NULL && count != 0) {
|
|
pr_info("[factroy_charge][store_cmd_charge_disable] buf is %s and size is %zu\n", buf, count);
|
|
ret = kstrtouint(buf, 10, ®);
|
|
pr_err("[factroy_charge][store_cmd_charge_disable] reg=%d \n", reg);
|
|
if(reg == MTK_DEBUG_TEMP_EN_CMD) {
|
|
info->debug_temp_en = true;
|
|
info->debug_temp = 25;
|
|
} else if(reg == MTK_DEBUG_TEMP_DIS_CMD){
|
|
info->debug_temp_en = false;
|
|
info->debug_temp = 25;
|
|
} else{
|
|
info->debug_temp = reg;
|
|
}
|
|
}
|
|
failed:
|
|
return count;
|
|
}
|
|
|
|
static struct class * factory_charger_class;
|
|
static struct class_attribute factory_charger_class_attrs[] = {
|
|
#if IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT)
|
|
__ATTR(chg2_exist, S_IRUGO, chg2_exist_show, NULL),
|
|
#endif /* CONFIG_SECOND_CHARGER_SUPPORT */
|
|
__ATTR(fast_charger, S_IRUGO, fast_charger_show, NULL),
|
|
__ATTR(fast_charger_enable, S_IRUGO, fast_charger_enable_show, NULL),
|
|
__ATTR(fast_charger_support, S_IRUGO, fast_charger_support_show, NULL),
|
|
__ATTR(fast_charger_pwr, S_IRUGO,fast_charger_power_show, NULL),
|
|
__ATTR(charger_type, S_IRUGO, charger_type_show, NULL),
|
|
__ATTR(charger_algo, S_IRUGO, charger_algo_show, NULL),
|
|
//__ATTR(cmd_charge_disable, S_IRUGO | S_IWUSR, show_cmd_charge_disable, store_cmd_charge_disable),
|
|
__ATTR(cmd_debug_temp, S_IRUGO | S_IWUSR, show_cmd_debug_temp, store_cmd_debug_temp),
|
|
__ATTR_NULL,
|
|
};
|
|
|
|
static int factory_charger_sysfs_create(void)
|
|
{
|
|
int i = 0,ret = 0;
|
|
factory_charger_class = class_create(THIS_MODULE, "factory_charger");
|
|
if (IS_ERR(factory_charger_class))
|
|
return PTR_ERR(factory_charger_class);
|
|
for (i = 0; factory_charger_class_attrs[i].attr.name; i++) {
|
|
ret = class_create_file(factory_charger_class,&factory_charger_class_attrs[i]);
|
|
if (ret < 0)
|
|
{
|
|
pr_err("factory_charger sysfs create error !!\n");
|
|
return ret;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_FACTORY_CHARGE */
|
|
// drv add tankaikun, add facoryt charger class, 20231204 end
|
|
|
|
void mtk_charger_get_atm_mode(struct mtk_charger *info)
|
|
{
|
|
char atm_str[64] = {0};
|
|
char *ptr = NULL, *ptr_e = NULL;
|
|
char keyword[] = "androidboot.atm=";
|
|
int size = 0;
|
|
|
|
ptr = strstr(chg_get_cmd(), keyword);
|
|
if (ptr != 0) {
|
|
ptr_e = strstr(ptr, " ");
|
|
if (ptr_e == 0)
|
|
goto end;
|
|
|
|
size = ptr_e - (ptr + strlen(keyword));
|
|
if (size <= 0)
|
|
goto end;
|
|
strncpy(atm_str, ptr + strlen(keyword), size);
|
|
atm_str[size] = '\0';
|
|
chr_err("%s: atm_str: %s\n", __func__, atm_str);
|
|
|
|
if (!strncmp(atm_str, "enable", strlen("enable")))
|
|
info->atm_enabled = true;
|
|
}
|
|
end:
|
|
chr_err("%s: atm_enabled = %d\n", __func__, info->atm_enabled);
|
|
}
|
|
|
|
static int psy_charger_property_is_writeable(struct power_supply *psy,
|
|
enum power_supply_property psp)
|
|
{
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
return 1;
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
|
return 1;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static enum power_supply_property charger_psy_properties[] = {
|
|
POWER_SUPPLY_PROP_ONLINE,
|
|
POWER_SUPPLY_PROP_PRESENT,
|
|
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
|
POWER_SUPPLY_PROP_TEMP,
|
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
|
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
|
};
|
|
|
|
static int psy_charger_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp, union power_supply_propval *val)
|
|
{
|
|
struct mtk_charger *info;
|
|
struct charger_device *chg;
|
|
struct charger_data *pdata;
|
|
int ret = 0;
|
|
struct chg_alg_device *alg = NULL;
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (info == NULL) {
|
|
chr_err("%s: get info failed\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
chr_debug("%s psp:%d\n", __func__, psp);
|
|
|
|
if (info->psy1 != NULL &&
|
|
info->psy1 == psy)
|
|
chg = info->chg1_dev;
|
|
/* prize liuyong add for usb supply config, 20231122, start */
|
|
else if (info->usb_psy != NULL &&
|
|
info->usb_psy == psy)
|
|
chg = info->chg1_dev;
|
|
/* prize liuyong add for usb supply config, 20231122, end */
|
|
else if (info->psy2 != NULL &&
|
|
info->psy2 == psy)
|
|
chg = info->chg2_dev;
|
|
else if (info->psy_dvchg1 != NULL && info->psy_dvchg1 == psy)
|
|
chg = info->dvchg1_dev;
|
|
else if (info->psy_dvchg2 != NULL && info->psy_dvchg2 == psy)
|
|
chg = info->dvchg2_dev;
|
|
else if (info->psy_hvdvchg1 != NULL && info->psy_hvdvchg1 == psy)
|
|
chg = info->hvdvchg1_dev;
|
|
else if (info->psy_hvdvchg2 != NULL && info->psy_hvdvchg2 == psy)
|
|
chg = info->hvdvchg2_dev;
|
|
else {
|
|
chr_err("%s fail\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
if (chg == info->dvchg1_dev) {
|
|
val->intval = false;
|
|
alg = get_chg_alg_by_name("pe5");
|
|
if (alg == NULL)
|
|
chr_err("get pe5 fail\n");
|
|
else {
|
|
ret = chg_alg_is_algo_ready(alg);
|
|
if (ret == ALG_RUNNING)
|
|
val->intval = true;
|
|
}
|
|
break;
|
|
}
|
|
/* prize liuyong add for usb supply config, 20231122, start */
|
|
if (info->chr_type == POWER_SUPPLY_TYPE_USB ||
|
|
info->chr_type == POWER_SUPPLY_TYPE_USB_CDP)
|
|
val->intval = is_charger_exist(info);
|
|
/* prize liuyong add for usb supply config, 20231122, start */
|
|
break;
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
if (chg != NULL)
|
|
val->intval = true;
|
|
else
|
|
val->intval = false;
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
val->intval = info->enable_hv_charging;
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
|
val->intval = get_vbus(info);
|
|
break;
|
|
case POWER_SUPPLY_PROP_TEMP:
|
|
if (chg == info->chg1_dev)
|
|
val->intval =
|
|
info->chg_data[CHG1_SETTING].junction_temp_max * 10;
|
|
else if (chg == info->chg2_dev)
|
|
val->intval =
|
|
info->chg_data[CHG2_SETTING].junction_temp_max * 10;
|
|
else if (chg == info->dvchg1_dev) {
|
|
pdata = &info->chg_data[DVCHG1_SETTING];
|
|
val->intval = pdata->junction_temp_max;
|
|
} else if (chg == info->dvchg2_dev) {
|
|
pdata = &info->chg_data[DVCHG2_SETTING];
|
|
val->intval = pdata->junction_temp_max;
|
|
} else
|
|
val->intval = -127;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
|
val->intval = get_charger_charging_current(info, chg);
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
|
val->intval = get_charger_input_current(info, chg);
|
|
break;
|
|
case POWER_SUPPLY_PROP_USB_TYPE:
|
|
val->intval = info->chr_type;
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
|
|
val->intval = get_charger_zcv(info, chg);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_charger_enable_power_path(struct mtk_charger *info,
|
|
int idx, bool en)
|
|
{
|
|
int ret = 0;
|
|
bool is_en = true;
|
|
struct charger_device *chg_dev = NULL;
|
|
|
|
if (!info)
|
|
return -EINVAL;
|
|
|
|
switch (idx) {
|
|
case CHG1_SETTING:
|
|
chg_dev = get_charger_by_name("primary_chg");
|
|
break;
|
|
case CHG2_SETTING:
|
|
chg_dev = get_charger_by_name("secondary_chg");
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ERR_OR_NULL(chg_dev)) {
|
|
chr_err("%s: chg_dev not found\n", __func__);
|
|
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;
|
|
}
|
|
|
|
static int mtk_charger_force_disable_power_path(struct mtk_charger *info,
|
|
int idx, bool disable)
|
|
{
|
|
int ret = 0;
|
|
struct charger_device *chg_dev = NULL;
|
|
|
|
if (!info)
|
|
return -EINVAL;
|
|
|
|
switch (idx) {
|
|
case CHG1_SETTING:
|
|
chg_dev = get_charger_by_name("primary_chg");
|
|
break;
|
|
case CHG2_SETTING:
|
|
chg_dev = get_charger_by_name("secondary_chg");
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ERR_OR_NULL(chg_dev)) {
|
|
chr_err("%s: chg_dev not found\n", __func__);
|
|
return -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;
|
|
}
|
|
|
|
int psy_charger_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct mtk_charger *info;
|
|
int idx;
|
|
|
|
chr_err("%s: prop:%d %d\n", __func__, psp, val->intval);
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (info == NULL) {
|
|
chr_err("%s: failed to get info\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (info->psy1 != NULL &&
|
|
info->psy1 == psy)
|
|
idx = CHG1_SETTING;
|
|
/* prize liuyong add for usb supply config, 20231122, start */
|
|
else if (info->usb_psy != NULL &&
|
|
info->usb_psy == psy)
|
|
idx = CHG1_SETTING;
|
|
/* prize liuyong add for usb supply config, 20231122, end */
|
|
else if (info->psy2 != NULL &&
|
|
info->psy2 == psy)
|
|
idx = CHG2_SETTING;
|
|
else if (info->psy_dvchg1 != NULL && info->psy_dvchg1 == psy)
|
|
idx = DVCHG1_SETTING;
|
|
else if (info->psy_dvchg2 != NULL && info->psy_dvchg2 == psy)
|
|
idx = DVCHG2_SETTING;
|
|
else {
|
|
chr_err("%s fail\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
if (val->intval > 0)
|
|
info->enable_hv_charging = true;
|
|
else
|
|
info->enable_hv_charging = false;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
|
info->chg_data[idx].thermal_charging_current_limit =
|
|
val->intval;
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
|
info->chg_data[idx].thermal_input_current_limit =
|
|
val->intval;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
|
if (val->intval > 0)
|
|
mtk_charger_enable_power_path(info, idx, false);
|
|
else
|
|
mtk_charger_enable_power_path(info, idx, true);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
|
if (val->intval > 0)
|
|
mtk_charger_force_disable_power_path(info, idx, true);
|
|
else
|
|
mtk_charger_force_disable_power_path(info, idx, false);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
_wake_up_charger(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_charger_external_power_changed(struct power_supply *psy)
|
|
{
|
|
struct mtk_charger *info;
|
|
union power_supply_propval prop = {0};
|
|
union power_supply_propval prop2 = {0};
|
|
union power_supply_propval vbat0 = {0};
|
|
struct power_supply *chg_psy = NULL;
|
|
int ret;
|
|
|
|
info = (struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (info == NULL) {
|
|
pr_notice("%s: failed to get info\n", __func__);
|
|
return;
|
|
}
|
|
chg_psy = info->chg_psy;
|
|
|
|
if (IS_ERR_OR_NULL(chg_psy)) {
|
|
pr_notice("%s Couldn't get chg_psy\n", __func__);
|
|
chg_psy = power_supply_get_by_name("primary_chg");
|
|
info->chg_psy = chg_psy;
|
|
} else {
|
|
ret = power_supply_get_property(chg_psy,
|
|
POWER_SUPPLY_PROP_ONLINE, &prop);
|
|
ret = power_supply_get_property(chg_psy,
|
|
POWER_SUPPLY_PROP_USB_TYPE, &prop2);
|
|
ret = power_supply_get_property(chg_psy,
|
|
POWER_SUPPLY_PROP_ENERGY_EMPTY, &vbat0);
|
|
}
|
|
|
|
if (info->vbat0_flag != vbat0.intval) {
|
|
if (vbat0.intval) {
|
|
info->enable_vbat_mon = false;
|
|
charger_dev_enable_6pin_battery_charging(info->chg1_dev, false);
|
|
} else
|
|
info->enable_vbat_mon = info->enable_vbat_mon_bak;
|
|
|
|
info->vbat0_flag = vbat0.intval;
|
|
}
|
|
|
|
pr_notice("%s event, name:%s online:%d type:%d vbus:%d\n", __func__,
|
|
psy->desc->name, prop.intval, prop2.intval,
|
|
get_vbus(info));
|
|
|
|
_wake_up_charger(info);
|
|
}
|
|
|
|
int notify_adapter_event(struct notifier_block *notifier,
|
|
unsigned long evt, void *val)
|
|
{
|
|
struct mtk_charger *pinfo = NULL;
|
|
|
|
chr_err("%s %lu\n", __func__, evt);
|
|
|
|
pinfo = container_of(notifier,
|
|
struct mtk_charger, pd_nb);
|
|
|
|
switch (evt) {
|
|
case MTK_PD_CONNECT_NONE:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify Detach\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_NONE;
|
|
pinfo->pd_reset = false;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
mtk_chg_alg_notify_call(pinfo, EVT_DETACH, 0);
|
|
/* reset PE40 */
|
|
break;
|
|
|
|
case MTK_PD_CONNECT_HARD_RESET:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify HardReset\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_NONE;
|
|
pinfo->pd_reset = true;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
mtk_chg_alg_notify_call(pinfo, EVT_HARDRESET, 0);
|
|
_wake_up_charger(pinfo);
|
|
/* reset PE40 */
|
|
break;
|
|
|
|
case MTK_PD_CONNECT_PE_READY_SNK:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify fixe voltage ready\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK;
|
|
pinfo->pd_reset = false;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
/* PD is ready */
|
|
break;
|
|
|
|
case MTK_PD_CONNECT_PE_READY_SNK_PD30:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify PD30 ready\r\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_PD30;
|
|
pinfo->pd_reset = false;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
/* PD30 is ready */
|
|
break;
|
|
|
|
case MTK_PD_CONNECT_PE_READY_SNK_APDO:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify APDO Ready\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_APDO;
|
|
pinfo->pd_reset = false;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
/* PE40 is ready */
|
|
_wake_up_charger(pinfo);
|
|
break;
|
|
|
|
case MTK_PD_CONNECT_TYPEC_ONLY_SNK:
|
|
mutex_lock(&pinfo->pd_lock);
|
|
chr_err("PD Notify Type-C Ready\n");
|
|
pinfo->pd_type = MTK_PD_CONNECT_TYPEC_ONLY_SNK;
|
|
pinfo->pd_reset = false;
|
|
mutex_unlock(&pinfo->pd_lock);
|
|
/* type C is ready */
|
|
_wake_up_charger(pinfo);
|
|
break;
|
|
case MTK_TYPEC_WD_STATUS:
|
|
chr_err("wd status = %d\n", *(bool *)val);
|
|
pinfo->water_detected = *(bool *)val;
|
|
if (pinfo->water_detected == true) {
|
|
pinfo->notify_code |= CHG_TYPEC_WD_STATUS;
|
|
pinfo->record_water_detected = true;
|
|
} else
|
|
pinfo->notify_code &= ~CHG_TYPEC_WD_STATUS;
|
|
mtk_chgstat_notify(pinfo);
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
int chg_alg_event(struct notifier_block *notifier,
|
|
unsigned long event, void *data)
|
|
{
|
|
chr_err("%s: evt:%d\n", __func__, event);
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_DRM_MEDIATEK)
|
|
/* prize LiuYong 20240219, modify to full time start */
|
|
bool get_screen_on_status()
|
|
{
|
|
struct power_supply *psy;
|
|
static struct mtk_charger *info;
|
|
|
|
if (info == NULL) {
|
|
psy = power_supply_get_by_name("mtk-master-charger");
|
|
if (psy == NULL)
|
|
return false;
|
|
else {
|
|
info =
|
|
(struct mtk_charger *)power_supply_get_drvdata(psy);
|
|
if (info == NULL)
|
|
return false;
|
|
else
|
|
return info->is_screen_on;
|
|
}
|
|
} else
|
|
return info->is_screen_on;
|
|
}
|
|
EXPORT_SYMBOL(get_screen_on_status);
|
|
/* prize LiuYong 20240219, modify to full time end */
|
|
static int charger_disp_notifier_callback(struct notifier_block *nb,
|
|
unsigned long value, void *v)
|
|
{
|
|
struct mtk_charger *info = container_of(nb, struct mtk_charger, disp_notifier);
|
|
int *data = (int *)v;
|
|
|
|
if (info && v) {
|
|
//cts_err("%s IN", __func__);
|
|
if (value == MTK_DISP_EARLY_EVENT_BLANK) {
|
|
if (*data == MTK_DISP_BLANK_POWERDOWN) {
|
|
info->is_screen_on = false;
|
|
}
|
|
} else if (value == MTK_DISP_EVENT_BLANK) {
|
|
if (*data == MTK_DISP_BLANK_UNBLANK) {
|
|
info->is_screen_on = true;
|
|
}
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
static char *mtk_charger_supplied_to[] = {
|
|
"battery"
|
|
};
|
|
|
|
static int mtk_charger_probe(struct platform_device *pdev)
|
|
{
|
|
struct mtk_charger *info = NULL;
|
|
int i;
|
|
char *name = NULL;
|
|
/* prize add by liuyong, modify for screen on charging 20230315 start */
|
|
#if IS_ENABLED(CONFIG_DRM_MEDIATEK)
|
|
int ret;
|
|
#endif
|
|
/* prize add by liuyong, modify for screen on charging 20230315 end */
|
|
|
|
chr_err("%s: starts\n", __func__);
|
|
|
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
|
if (!info)
|
|
return -ENOMEM;
|
|
platform_set_drvdata(pdev, info);
|
|
info->pdev = pdev;
|
|
|
|
mtk_charger_parse_dt(info, &pdev->dev);
|
|
|
|
mutex_init(&info->cable_out_lock);
|
|
mutex_init(&info->charger_lock);
|
|
mutex_init(&info->pd_lock);
|
|
for (i = 0; i < CHG2_SETTING + 1; i++) {
|
|
mutex_init(&info->pp_lock[i]);
|
|
info->force_disable_pp[i] = false;
|
|
info->enable_pp[i] = true;
|
|
}
|
|
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s",
|
|
"charger suspend wakelock");
|
|
info->charger_wakelock =
|
|
wakeup_source_register(NULL, name);
|
|
spin_lock_init(&info->slock);
|
|
|
|
init_waitqueue_head(&info->wait_que);
|
|
info->polling_interval = CHARGING_INTERVAL;
|
|
mtk_charger_init_timer(info);
|
|
#ifdef CONFIG_PM
|
|
if (register_pm_notifier(&info->pm_notifier)) {
|
|
chr_err("%s: register pm failed\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
info->pm_notifier.notifier_call = charger_pm_event;
|
|
#endif /* CONFIG_PM */
|
|
srcu_init_notifier_head(&info->evt_nh);
|
|
mtk_charger_setup_files(pdev);
|
|
mtk_charger_get_atm_mode(info);
|
|
|
|
for (i = 0; i < CHGS_SETTING_MAX; i++) {
|
|
info->chg_data[i].thermal_charging_current_limit = -1;
|
|
info->chg_data[i].thermal_input_current_limit = -1;
|
|
info->chg_data[i].input_current_limit_by_aicl = -1;
|
|
}
|
|
info->enable_hv_charging = true;
|
|
|
|
info->psy_desc1.name = "mtk-master-charger";
|
|
info->psy_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_desc1.properties = charger_psy_properties;
|
|
info->psy_desc1.num_properties = ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_desc1.get_property = psy_charger_get_property;
|
|
info->psy_desc1.set_property = psy_charger_set_property;
|
|
info->psy_desc1.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_desc1.external_power_changed =
|
|
mtk_charger_external_power_changed;
|
|
info->psy_cfg1.drv_data = info;
|
|
info->psy_cfg1.supplied_to = mtk_charger_supplied_to;
|
|
info->psy_cfg1.num_supplicants = ARRAY_SIZE(mtk_charger_supplied_to);
|
|
info->psy1 = power_supply_register(&pdev->dev, &info->psy_desc1,
|
|
&info->psy_cfg1);
|
|
|
|
info->chg_psy = power_supply_get_by_name("primary_chg");
|
|
if (IS_ERR_OR_NULL(info->chg_psy))
|
|
chr_err("%s: devm power fail to get chg_psy\n", __func__);
|
|
|
|
info->bc12_psy = power_supply_get_by_name("primary_chg");
|
|
if (IS_ERR_OR_NULL(info->bc12_psy))
|
|
chr_err("%s: devm power fail to get bc12_psy\n", __func__);
|
|
|
|
info->bat_psy = devm_power_supply_get_by_phandle(&pdev->dev,
|
|
"gauge");
|
|
if (IS_ERR_OR_NULL(info->bat_psy))
|
|
chr_err("%s: devm power fail to get bat_psy\n", __func__);
|
|
|
|
if (IS_ERR(info->psy1))
|
|
chr_err("register psy1 fail:%ld\n",
|
|
PTR_ERR(info->psy1));
|
|
|
|
info->psy_desc2.name = "mtk-slave-charger";
|
|
info->psy_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_desc2.properties = charger_psy_properties;
|
|
info->psy_desc2.num_properties = ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_desc2.get_property = psy_charger_get_property;
|
|
info->psy_desc2.set_property = psy_charger_set_property;
|
|
info->psy_desc2.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_cfg2.drv_data = info;
|
|
info->psy2 = power_supply_register(&pdev->dev, &info->psy_desc2,
|
|
&info->psy_cfg2);
|
|
|
|
if (IS_ERR(info->psy2))
|
|
chr_err("register psy2 fail:%ld\n",
|
|
PTR_ERR(info->psy2));
|
|
|
|
info->psy_dvchg_desc1.name = "mtk-mst-div-chg";
|
|
info->psy_dvchg_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_dvchg_desc1.properties = charger_psy_properties;
|
|
info->psy_dvchg_desc1.num_properties =
|
|
ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_dvchg_desc1.get_property = psy_charger_get_property;
|
|
info->psy_dvchg_desc1.set_property = psy_charger_set_property;
|
|
info->psy_dvchg_desc1.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_dvchg_cfg1.drv_data = info;
|
|
info->psy_dvchg1 = power_supply_register(&pdev->dev,
|
|
&info->psy_dvchg_desc1,
|
|
&info->psy_dvchg_cfg1);
|
|
if (IS_ERR(info->psy_dvchg1))
|
|
chr_err("register psy dvchg1 fail:%ld\n",
|
|
PTR_ERR(info->psy_dvchg1));
|
|
|
|
info->psy_dvchg_desc2.name = "mtk-slv-div-chg";
|
|
info->psy_dvchg_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_dvchg_desc2.properties = charger_psy_properties;
|
|
info->psy_dvchg_desc2.num_properties =
|
|
ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_dvchg_desc2.get_property = psy_charger_get_property;
|
|
info->psy_dvchg_desc2.set_property = psy_charger_set_property;
|
|
info->psy_dvchg_desc2.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_dvchg_cfg2.drv_data = info;
|
|
info->psy_dvchg2 = power_supply_register(&pdev->dev,
|
|
&info->psy_dvchg_desc2,
|
|
&info->psy_dvchg_cfg2);
|
|
if (IS_ERR(info->psy_dvchg2))
|
|
chr_err("register psy dvchg2 fail:%ld\n",
|
|
PTR_ERR(info->psy_dvchg2));
|
|
|
|
info->psy_hvdvchg_desc1.name = "mtk-mst-hvdiv-chg";
|
|
info->psy_hvdvchg_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_hvdvchg_desc1.properties = charger_psy_properties;
|
|
info->psy_hvdvchg_desc1.num_properties =
|
|
ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_hvdvchg_desc1.get_property = psy_charger_get_property;
|
|
info->psy_hvdvchg_desc1.set_property = psy_charger_set_property;
|
|
info->psy_hvdvchg_desc1.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_hvdvchg_cfg1.drv_data = info;
|
|
info->psy_hvdvchg1 = power_supply_register(&pdev->dev,
|
|
&info->psy_hvdvchg_desc1,
|
|
&info->psy_hvdvchg_cfg1);
|
|
if (IS_ERR(info->psy_hvdvchg1))
|
|
chr_err("register psy hvdvchg1 fail:%ld\n",
|
|
PTR_ERR(info->psy_hvdvchg1));
|
|
|
|
info->psy_hvdvchg_desc2.name = "mtk-slv-hvdiv-chg";
|
|
info->psy_hvdvchg_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
info->psy_hvdvchg_desc2.properties = charger_psy_properties;
|
|
info->psy_hvdvchg_desc2.num_properties =
|
|
ARRAY_SIZE(charger_psy_properties);
|
|
info->psy_hvdvchg_desc2.get_property = psy_charger_get_property;
|
|
info->psy_hvdvchg_desc2.set_property = psy_charger_set_property;
|
|
info->psy_hvdvchg_desc2.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->psy_hvdvchg_cfg2.drv_data = info;
|
|
info->psy_hvdvchg2 = power_supply_register(&pdev->dev,
|
|
&info->psy_hvdvchg_desc2,
|
|
&info->psy_hvdvchg_cfg2);
|
|
if (IS_ERR(info->psy_hvdvchg2))
|
|
chr_err("register psy hvdvchg2 fail:%ld\n",
|
|
PTR_ERR(info->psy_hvdvchg2));
|
|
/* prize liuyong, add for charging config, 20231018, start*/
|
|
info->usb_desc.name = "usb";
|
|
info->usb_desc.type = POWER_SUPPLY_TYPE_USB;
|
|
info->usb_desc.properties = charger_psy_properties;
|
|
info->usb_desc.num_properties =
|
|
ARRAY_SIZE(charger_psy_properties);
|
|
info->usb_desc.get_property = psy_charger_get_property;
|
|
info->usb_desc.set_property = psy_charger_set_property;
|
|
info->usb_desc.property_is_writeable =
|
|
psy_charger_property_is_writeable;
|
|
info->usb_cfg.drv_data = info;
|
|
info->usb_psy = power_supply_register(&pdev->dev,
|
|
&info->usb_desc,
|
|
&info->usb_cfg);
|
|
if (IS_ERR(info->usb_psy))
|
|
chr_err("register psy usb_psy fail:%ld\n",
|
|
PTR_ERR(info->usb_psy));
|
|
/* prize liuyong, add for charging config, 20231018, end*/
|
|
|
|
info->log_level = CHRLOG_ERROR_LEVEL;
|
|
|
|
info->pd_adapter = get_adapter_by_name("pd_adapter");
|
|
if (!info->pd_adapter)
|
|
chr_err("%s: No pd adapter found\n", __func__);
|
|
else {
|
|
info->pd_nb.notifier_call = notify_adapter_event;
|
|
register_adapter_device_notifier(info->pd_adapter,
|
|
&info->pd_nb);
|
|
}
|
|
|
|
// drv add tankaikun, add facoryt charger class, 20231204, start
|
|
#if IS_ENABLED(CONFIG_FACTORY_CHARGE)
|
|
factory_charger_sysfs_create();
|
|
#endif /*CONFIG_FACTORY_CHARGE*/
|
|
// drv add tankaikun, add facoryt charger class, 20231204, end
|
|
|
|
sc_init(&info->sc);
|
|
info->chg_alg_nb.notifier_call = chg_alg_event;
|
|
|
|
info->fast_charging_indicator = 0;
|
|
info->enable_meta_current_limit = 1;
|
|
info->is_charging = false;
|
|
info->safety_timer_cmd = -1;
|
|
|
|
/* 8 = KERNEL_POWER_OFF_CHARGING_BOOT */
|
|
/* 9 = LOW_POWER_OFF_CHARGING_BOOT */
|
|
if (info != NULL && info->bootmode != 8 && info->bootmode != 9)
|
|
mtk_charger_force_disable_power_path(info, CHG1_SETTING, true);
|
|
|
|
/* prize add by liuyong, modify for screen on charging 20230315 start */
|
|
#if IS_ENABLED(CONFIG_DRM_MEDIATEK)
|
|
info->disp_notifier.notifier_call = charger_disp_notifier_callback;
|
|
ret = mtk_disp_notifier_register("screen monitor", &info->disp_notifier);
|
|
if (ret) {
|
|
pr_err("Failed to register screen monitor notifier client:%d", ret);
|
|
//goto err_register_disp_notif_failed;
|
|
}
|
|
else{
|
|
pr_err("gezi screen monitor register success.\n");
|
|
}
|
|
#endif
|
|
/* prize add by liuyong, modify for screen on charging 20230315 end */
|
|
kthread_run(charger_routine_thread, info, "charger_thread");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_charger_remove(struct platform_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_charger_shutdown(struct platform_device *dev)
|
|
{
|
|
struct mtk_charger *info = platform_get_drvdata(dev);
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_ALG_NO; i++) {
|
|
if (info->alg[i] == NULL)
|
|
continue;
|
|
chg_alg_stop_algo(info->alg[i]);
|
|
}
|
|
}
|
|
|
|
static const struct of_device_id mtk_charger_of_match[] = {
|
|
{.compatible = "mediatek,charger",},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, mtk_charger_of_match);
|
|
|
|
struct platform_device mtk_charger_device = {
|
|
.name = "charger",
|
|
.id = -1,
|
|
};
|
|
|
|
static struct platform_driver mtk_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(&mtk_charger_driver);
|
|
}
|
|
module_init(mtk_charger_init);
|
|
|
|
static void __exit mtk_charger_exit(void)
|
|
{
|
|
platform_driver_unregister(&mtk_charger_driver);
|
|
}
|
|
module_exit(mtk_charger_exit);
|
|
|
|
|
|
MODULE_AUTHOR("wy.chuang <wy.chuang@mediatek.com>");
|
|
MODULE_DESCRIPTION("MTK Charger Driver");
|
|
MODULE_LICENSE("GPL");
|