kernel_samsung_a34x-permissive/drivers/power/supply/mediatek/charger/mtk_charger.c
2024-04-28 15:51:13 +02:00

4268 lines
109 KiB
C

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