kernel-brax3-ubuntu-touch/drivers/power/supply/upm6910_charger.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

2301 lines
60 KiB
C
Executable file

/*
* UPM6910D battery charging driver
*
* Copyright (C) 2018 Unisemipower
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#define pr_fmt(fmt) "[upm6910]:%s: " fmt, __func__
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/err.h>
#include <linux/bitops.h>
#include <linux/math64.h>
/* prize liuyong, add for fac test, 20231024, start */
#include "../../misc/mediatek/extcon/extcon-mtk-usb.h"
/* prize liuyong, add for fac test, 20231024, end */
#include <linux/phy/phy.h>
//#include <mt-plat/v1/charger_type.h>
//#include "mtk_charger_intf.h"
#include "upm6910_reg.h"
#include "upm6910.h"
#include "charger_class.h"
#include "mtk_charger.h"
//#include <mt-plat/upmu_common.h>
#define POWER_DETECT_ICHG 1020
#define POWER_DETECT_ICL 2000
#define POWER_DETECT_VINDPM 4500
/*prize liuyong, modify for kpoc charging, 20230410, start*/
#define KPOC_CHECK_DELAY 1000
#define PHY_MODE_BC11_SET 1
#define PHY_MODE_BC11_CLR 2
enum upm6910_usbsw {
USBSW_CHG,
USBSW_USB,
};
struct tag_bootmode {
u32 size;
u32 tag;
u32 bootmode;
u32 boottype;
};
/*prize liuyong, modify for kpoc charging, 20230410, end*/
enum {
PN_UPM6910D,
};
enum upm6910_part_no {
UPM6910D = 0x02,
};
static int pn_data[] = {
[PN_UPM6910D] = 0x02,
};
static char *pn_str[] = {
[PN_UPM6910D] = "upm6910d",
};
struct upm6910 {
struct device *dev;
struct i2c_client *client;
enum upm6910_part_no part_no;
int revision;
int input_current_limit;
int constant_chargevolt;
int term_current;
int input_current_max;
const char *chg_dev_name;
const char *eint_name;
bool chg_det_enable;
int chg_en_gpio;
int status;
int irq;
u32 intr_gpio;
struct mutex i2c_rw_lock;
struct mutex pe_access_lock;
bool charge_enabled; /* Register bit status */
bool power_good;
bool vbus_gd;
struct upm6910_platform_data *platform_data;
struct charger_device *chg_dev;
struct power_supply_desc psy_desc;
struct power_supply *psy;
struct power_supply *bat_psy;
struct power_supply *usb_psy;
struct power_supply *chg_psy;
struct delayed_work psy_dwork;
struct delayed_work prob_dwork;
int psy_usb_type;
/*prize liuyong, modify for kpoc charging, 20230410, start*/
u32 bootmode;
u32 boottype;
struct delayed_work kpoc_dwork;
struct work_struct force_dpdm_work;
struct workqueue_struct *wq;
struct power_supply *batt_psy;
/*prize liuyong, modify for kpoc charging, 20230410, end*/
bool is_otg_en; // prize liuyong, add for boot up otg state,20240229
};
static const struct charger_properties upm6910_chg_props = {
.alias_name = "upm6910",
};
static int __upm6910_read_reg(struct upm6910 *upm, u8 reg, u8 *data)
{
s32 ret;
ret = i2c_smbus_read_byte_data(upm->client, reg);
if (ret < 0) {
pr_err("i2c read fail: can't read from reg 0x%02X\n", reg);
return ret;
}
*data = (u8) ret;
return 0;
}
static int __upm6910_write_reg(struct upm6910 *upm, int reg, u8 val)
{
s32 ret;
ret = i2c_smbus_write_byte_data(upm->client, reg, val);
if (ret < 0) {
pr_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n",
val, reg, ret);
return ret;
}
return 0;
}
static int upm6910_read_byte(struct upm6910 *upm, u8 reg, u8 *data)
{
int ret;
mutex_lock(&upm->i2c_rw_lock);
ret = __upm6910_read_reg(upm, reg, data);
mutex_unlock(&upm->i2c_rw_lock);
return ret;
}
static int upm6910_write_byte(struct upm6910 *upm, u8 reg, u8 data)
{
int ret;
mutex_lock(&upm->i2c_rw_lock);
ret = __upm6910_write_reg(upm, reg, data);
mutex_unlock(&upm->i2c_rw_lock);
if (ret)
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
return ret;
}
static int upm6910_update_bits(struct upm6910 *upm, u8 reg, u8 mask, u8 data)
{
int ret;
u8 tmp;
mutex_lock(&upm->i2c_rw_lock);
ret = __upm6910_read_reg(upm, reg, &tmp);
if (ret) {
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
goto out;
}
tmp &= ~mask;
tmp |= data & mask;
ret = __upm6910_write_reg(upm, reg, tmp);
if (ret)
pr_err("Failed: reg=%02X, ret=%d\n", reg, ret);
out:
mutex_unlock(&upm->i2c_rw_lock);
return ret;
}
static int upm6910_enable_otg(struct upm6910 *upm)
{
u8 val = REG01_OTG_ENABLE << REG01_OTG_CONFIG_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_01, REG01_OTG_CONFIG_MASK,
val);
}
static int upm6910_disable_otg(struct upm6910 *upm)
{
u8 val = REG01_OTG_DISABLE << REG01_OTG_CONFIG_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_01, REG01_OTG_CONFIG_MASK,
val);
}
static int upm6910_enable_charger(struct upm6910 *upm)
{
int ret;
u8 val = REG01_CHG_ENABLE << REG01_CHG_CONFIG_SHIFT;
ret =
upm6910_update_bits(upm, UPM6910_REG_01, REG01_CHG_CONFIG_MASK, val);
return ret;
}
static int upm6910_disable_charger(struct upm6910 *upm)
{
int ret;
u8 val = REG01_CHG_DISABLE << REG01_CHG_CONFIG_SHIFT;
ret =
upm6910_update_bits(upm, UPM6910_REG_01, REG01_CHG_CONFIG_MASK, val);
return ret;
}
int upm6910_set_chargecurrent(struct upm6910 *upm, int curr)
{
u8 ichg;
if (curr < REG02_ICHG_BASE)
curr = REG02_ICHG_BASE;
if (curr < REG02_ICHG_MIN)
curr = REG02_ICHG_MIN;
ichg = (curr - REG02_ICHG_BASE) / REG02_ICHG_LSB;
return upm6910_update_bits(upm, UPM6910_REG_02, REG02_ICHG_MASK,
ichg << REG02_ICHG_SHIFT);
}
int upm6910_set_term_current(struct upm6910 *upm, int curr)
{
u8 iterm;
if (curr < REG03_ITERM_BASE)
curr = REG03_ITERM_BASE;
iterm = (curr - REG03_ITERM_BASE) / REG03_ITERM_LSB;
return upm6910_update_bits(upm, UPM6910_REG_03, REG03_ITERM_MASK,
iterm << REG03_ITERM_SHIFT);
}
EXPORT_SYMBOL_GPL(upm6910_set_term_current);
int upm6910_set_prechg_current(struct upm6910 *upm, int curr)
{
u8 iprechg;
if (curr < REG03_IPRECHG_BASE)
curr = REG03_IPRECHG_BASE;
iprechg = (curr - REG03_IPRECHG_BASE) / REG03_IPRECHG_LSB;
return upm6910_update_bits(upm, UPM6910_REG_03, REG03_IPRECHG_MASK,
iprechg << REG03_IPRECHG_SHIFT);
}
EXPORT_SYMBOL_GPL(upm6910_set_prechg_current);
int upm6910_set_chargevolt(struct upm6910 *upm, int volt)
{
u8 val;
if (volt < REG04_VREG_BASE)
volt = REG04_VREG_BASE;
val = (volt - REG04_VREG_BASE) / REG04_VREG_LSB;
return upm6910_update_bits(upm, UPM6910_REG_04, REG04_VREG_MASK,
val << REG04_VREG_SHIFT);
}
int upm6910_set_input_volt_limit(struct upm6910 *upm, int volt)
{
u8 val;
if (volt < REG06_VINDPM_BASE)
volt = REG06_VINDPM_BASE;
val = (volt - REG06_VINDPM_BASE) / REG06_VINDPM_LSB;
return upm6910_update_bits(upm, UPM6910_REG_06, REG06_VINDPM_MASK,
val << REG06_VINDPM_SHIFT);
}
static int upm6910_get_input_volt_limit(struct upm6910 *upm, int *volt)
{
u8 val;
int ret;
int vindpm;
ret = upm6910_read_byte(upm, UPM6910_REG_06, &val);
if (ret == 0){
pr_err("Reg[%.2x] = 0x%.2x\n", UPM6910_REG_06, val);
}
pr_info("vindpm_reg_val:0x%X\n", val);
vindpm = (val & REG06_VINDPM_MASK) >> REG06_VINDPM_SHIFT;
vindpm = vindpm * REG06_VINDPM_LSB + REG06_VINDPM_BASE;
*volt = vindpm;
return ret;
}
int upm6910_set_input_current_limit(struct upm6910 *upm, int curr)
{
u8 val;
if (curr < REG00_IINLIM_BASE)
curr = REG00_IINLIM_BASE;
val = (curr - REG00_IINLIM_BASE) / REG00_IINLIM_LSB;
return upm6910_update_bits(upm, UPM6910_REG_00, REG00_IINLIM_MASK,
val << REG00_IINLIM_SHIFT);
}
int upm6910_set_watchdog_timer(struct upm6910 *upm, u8 timeout)
{
u8 temp;
temp = (u8) (((timeout -
REG05_WDT_BASE) / REG05_WDT_LSB) << REG05_WDT_SHIFT);
return upm6910_update_bits(upm, UPM6910_REG_05, REG05_WDT_MASK, temp);
}
EXPORT_SYMBOL_GPL(upm6910_set_watchdog_timer);
int upm6910_disable_watchdog_timer(struct upm6910 *upm)
{
u8 val = REG05_WDT_DISABLE << REG05_WDT_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_05, REG05_WDT_MASK, val);
}
EXPORT_SYMBOL_GPL(upm6910_disable_watchdog_timer);
int upm6910_reset_watchdog_timer(struct upm6910 *upm)
{
u8 val = REG01_WDT_RESET << REG01_WDT_RESET_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_01, REG01_WDT_RESET_MASK,
val);
}
EXPORT_SYMBOL_GPL(upm6910_reset_watchdog_timer);
int upm6910_reset_chip(struct upm6910 *upm)
{
int ret;
u8 val = REG0B_REG_RESET << REG0B_REG_RESET_SHIFT;
ret =
upm6910_update_bits(upm, UPM6910_REG_0B, REG0B_REG_RESET_MASK, val);
return ret;
}
EXPORT_SYMBOL_GPL(upm6910_reset_chip);
int upm6910_enter_hiz_mode(struct upm6910 *upm)
{
u8 val = REG00_HIZ_ENABLE << REG00_ENHIZ_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_00, REG00_ENHIZ_MASK, val);
}
EXPORT_SYMBOL_GPL(upm6910_enter_hiz_mode);
int upm6910_exit_hiz_mode(struct upm6910 *upm)
{
u8 val = REG00_HIZ_DISABLE << REG00_ENHIZ_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_00, REG00_ENHIZ_MASK, val);
}
EXPORT_SYMBOL_GPL(upm6910_exit_hiz_mode);
int upm6910_set_hiz_mode(struct charger_device *chg_dev, bool en)
{
int ret;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
if (en)
ret = upm6910_enter_hiz_mode(upm);
else
ret = upm6910_exit_hiz_mode(upm);
pr_err("%s hiz mode %s\n", en ? "enable" : "disable",
!ret ? "successfully" : "failed");
return ret;
}
EXPORT_SYMBOL_GPL(upm6910_set_hiz_mode);
#if 0
static int upm6910_get_hiz_mode(struct charger_device *chg_dev)
{
int ret;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
u8 val;
ret = upm6910_read_byte(upm, UPM6910_REG_00, &val);
if (ret == 0){
pr_err("Reg[%.2x] = 0x%.2x\n", UPM6910_REG_00, val);
}
ret = (val & REG00_ENHIZ_MASK) >> REG00_ENHIZ_SHIFT;
pr_err("%s:hiz mode %s\n",__func__, ret ? "enabled" : "disabled");
return ret;
}
EXPORT_SYMBOL_GPL(upm6910_get_hiz_mode);
#endif
static int upm6910_do_event(struct charger_device *chg_dev, u32 event, u32 args)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
dev_info(upm->dev, "%s event = %d\n", __func__, event);
power_supply_changed(upm->psy);
return 0;
}
static int upm6910_enable_term(struct charger_device *chg_dev, bool enable)
{
u8 val;
int ret;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
if (enable)
val = REG05_TERM_ENABLE << REG05_EN_TERM_SHIFT;
else
val = REG05_TERM_DISABLE << REG05_EN_TERM_SHIFT;
ret = upm6910_update_bits(upm, UPM6910_REG_05, REG05_EN_TERM_MASK, val);
return ret;
}
//EXPORT_SYMBOL_GPL(upm6910_enable_term);
int upm6910_set_boost_current(struct upm6910 *upm, int curr)
{
u8 val;
val = REG02_BOOST_LIM_0P5A;
if (curr == BOOSTI_1200)
val = REG02_BOOST_LIM_1P2A;
return upm6910_update_bits(upm, UPM6910_REG_02, REG02_BOOST_LIM_MASK,
val << REG02_BOOST_LIM_SHIFT);
}
int upm6910_set_boost_voltage(struct upm6910 *upm, int volt)
{
u8 val;
if (volt == BOOSTV_4850)
val = REG06_BOOSTV_4P85V;
else if (volt == BOOSTV_5150)
val = REG06_BOOSTV_5P15V;
else if (volt == BOOSTV_5300)
val = REG06_BOOSTV_5P3V;
else
val = REG06_BOOSTV_5V;
return upm6910_update_bits(upm, UPM6910_REG_06, REG06_BOOSTV_MASK,
val << REG06_BOOSTV_SHIFT);
}
EXPORT_SYMBOL_GPL(upm6910_set_boost_voltage);
static int upm6910_set_acovp_threshold(struct upm6910 *upm, int volt)
{
u8 val;
if (volt == VAC_OVP_14000)
val = REG06_OVP_14P0V;
else if (volt == VAC_OVP_10500)
val = REG06_OVP_10P5V;
else if (volt == VAC_OVP_6500)
val = REG06_OVP_6P5V;
else
val = REG06_OVP_5P5V;
return upm6910_update_bits(upm, UPM6910_REG_06, REG06_OVP_MASK,
val << REG06_OVP_SHIFT);
}
//EXPORT_SYMBOL_GPL(upm6910_set_acovp_threshold);
static int upm6910_set_stat_ctrl(struct upm6910 *upm, int ctrl)
{
u8 val;
val = ctrl;
return upm6910_update_bits(upm, UPM6910_REG_00, REG00_STAT_CTRL_MASK,
val << REG00_STAT_CTRL_SHIFT);
}
static int upm6910_set_int_mask(struct upm6910 *upm, int mask)
{
u8 val;
val = mask;
return upm6910_update_bits(upm, UPM6910_REG_0A, REG0A_INT_MASK_MASK,
val << REG0A_INT_MASK_SHIFT);
}
static int upm6910_force_dpdm(struct upm6910 *upm)
{
const u8 val = REG07_FORCE_DPDM << REG07_FORCE_DPDM_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_07, REG07_FORCE_DPDM_MASK,
val);
}
/*
static int upm6910_enable_batfet(struct upm6910 *upm)
{
const u8 val = REG07_BATFET_ON << REG07_BATFET_DIS_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_07, REG07_BATFET_DIS_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_enable_batfet);
static int upm6910_disable_batfet(struct upm6910 *upm)
{
const u8 val = REG07_BATFET_OFF << REG07_BATFET_DIS_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_07, REG07_BATFET_DIS_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_disable_batfet);
static int upm6910_set_batfet_delay(struct upm6910 *upm, uint8_t delay)
{
u8 val;
if (delay == 0)
val = REG07_BATFET_DLY_0S;
else
val = REG07_BATFET_DLY_10S;
val <<= REG07_BATFET_DLY_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_07, REG07_BATFET_DLY_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_set_batfet_delay);
*/
/* prize liuyong add dpdm config, 20231129, start*/
static int upm6910_set_dp_voltage(struct upm6910 *upm, int val)
{
if ((val >= REG0C_DPDM_OUT_HIZ) && (val <= REG0C_DPDM_OUT_3P3V)) {
return upm6910_update_bits(upm, UPM6910_REG_0C, REG0C_DP_MUX_MASK,
val);
} else {
pr_err("dp voltage invalid\n");
return -EINVAL;
}
}
static int upm6910_set_dm_voltage(struct upm6910 *upm, int val)
{
if ((val >= REG0C_DPDM_OUT_HIZ) && (val <= REG0C_DPDM_OUT_3P3V)) {
return upm6910_update_bits(upm, UPM6910_REG_0C, REG0C_DM_MUX_MASK,
val);
} else {
pr_err("dm voltage invalid\n");
return -EINVAL;
}
}
/* prize liuyong add dpdm config, 20231129, start*/
static int upm6910_enable_safety_timer(struct upm6910 *upm)
{
const u8 val = REG05_CHG_TIMER_ENABLE << REG05_EN_TIMER_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_05, REG05_EN_TIMER_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_enable_safety_timer);
static int upm6910_disable_safety_timer(struct upm6910 *upm)
{
const u8 val = REG05_CHG_TIMER_DISABLE << REG05_EN_TIMER_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_05, REG05_EN_TIMER_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_disable_safety_timer);
/*
static int upm6910_enable_hvdcp(struct upm6910 *upm, bool en)
{
const u8 val = en << REG0C_EN_HVDCP_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_0C, REG0C_EN_HVDCP_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_enable_hvdcp);
static int upm6910_set_dp(struct upm6910 *upm, int dp_stat)
{
const u8 val = dp_stat << REG0C_DP_MUX_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_0C, REG0C_DP_MUX_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_set_dp);
static int upm6910_set_dm(struct upm6910 *upm, int dm_stat)
{
const u8 val = dm_stat << REG0C_DM_MUX_SHIFT;
return upm6910_update_bits(upm, UPM6910_REG_0C, REG0C_DM_MUX_MASK,
val);
}
//EXPORT_SYMBOL_GPL(upm6910_set_dm);
*/
/* prize liuyong remove for charger init, 20231129, start*/
#if 0
static int upm6910_dpdm_set_hidden_mode(struct upm6910 *upm)
{
int ret = 0;
union power_supply_propval propval;
if (!upm->bat_psy) {
upm->bat_psy = power_supply_get_by_name("battery");
if (!upm->bat_psy) {
pr_err("%s Couldn't get battery psy\n", __func__);
return -ENODEV;
}
}
ret = power_supply_get_property(upm->bat_psy,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &propval);
if (ret || propval.intval <= 3450000) {
pr_err("%s cannot goto hidden mode\n", __func__);
return -EINVAL;
}
ret = upm6910_write_byte(upm, 0xA9, 0x6E);
if (ret) {
pr_err("write 0xA9, 0x6E failed(%d)\n", ret);
}
ret = upm6910_write_byte(upm, 0xB1, 0x80);
if (ret) {
pr_err("write 0xB1, 0x80 failed(%d)\n", ret);
}
ret = upm6910_write_byte(upm, 0xB0, 0x21);
if (ret) {
pr_err("write 0xB0, 0x21 failed(%d)\n", ret);
}
ret = upm6910_write_byte(upm, 0xB1, 0x00);
if (ret) {
pr_err("write 0xB1, 0x00 failed(%d)\n", ret);
}
ret = upm6910_write_byte(upm, 0xA9, 0x00);
if (ret) {
pr_err("write 0xA9, 0x00 failed(%d)\n", ret);
}
return ret;
}
//EXPORT_SYMBOL_GPL(upm6910_dpdm_set_hidden_mode);
/* prize liuyong remove for charger init, 20231129, end*/
#endif
static int upm6910_get_ichg(struct charger_device *chg_dev, u32 *curr)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
u8 reg_val;
int ichg;
int ret;
ret = upm6910_read_byte(upm, UPM6910_REG_02, &reg_val);
if (!ret) {
ichg = (reg_val & REG02_ICHG_MASK) >> REG02_ICHG_SHIFT;
ichg = ichg * REG02_ICHG_LSB + REG02_ICHG_BASE;
*curr = ichg * 1000;
}
return ret;
}
static int upm6910_get_icl(struct charger_device *chg_dev, u32 *curr)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
u8 reg_val;
int icl;
int ret;
ret = upm6910_read_byte(upm, UPM6910_REG_00, &reg_val);
if (!ret) {
icl = (reg_val & REG00_IINLIM_MASK) >> REG00_IINLIM_SHIFT;
icl = icl * REG00_IINLIM_LSB + REG00_IINLIM_BASE;
*curr = icl * 1000;
}
return ret;
}
static struct upm6910_platform_data *upm6910_parse_dt(struct device_node *np,
struct upm6910 *upm)
{
int ret;
struct upm6910_platform_data *pdata;
/*prize liuyong, modify for kpoc charging, 20230410, start*/
struct device_node *boot_node = NULL;
struct tag_bootmode *tag = NULL;
/*prize liuyong, modify for kpoc charging, 20230410, end*/
pdata = devm_kzalloc(&upm->client->dev, sizeof(struct upm6910_platform_data),
GFP_KERNEL);
if (!pdata)
return NULL;
/*prize liuyong, modify for kpoc charging, 20230410, start*/
boot_node = of_parse_phandle(upm->dev->of_node, "bootmode", 0);
if (!boot_node)
pr_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)
pr_err("%s: failed to get atag,boot\n", __func__);
else {
pr_err("%s: size:0x%x tag:0x%x bootmode:0x%x boottype:0x%x\n",
__func__, tag->size, tag->tag,
tag->bootmode, tag->boottype);
upm->bootmode = tag->bootmode;
upm->boottype = tag->boottype;
}
}
/*prize liuyong, modify for kpoc charging, 20230410, end*/
if (of_property_read_string(np, "charger_name", &upm->chg_dev_name) < 0) {
upm->chg_dev_name = "primary_chg";
pr_warn("no charger name\n");
}
if (of_property_read_string(np, "eint_name", &upm->eint_name) < 0) {
upm->eint_name = "chr_stat";
pr_warn("no eint name\n");
}
ret = of_get_named_gpio(np, "upm,intr_gpio", 0);
if (ret < 0) {
pr_err("%s no upm,intr_gpio(%d)\n",
__func__, ret);
} else
upm->intr_gpio = ret;
pr_info("%s intr_gpio = %u\n",
__func__, upm->intr_gpio);
upm->chg_det_enable =
of_property_read_bool(np, "upm,upm6910,charge-detect-enable");
ret = of_property_read_u32(np, "upm,upm6910,usb-vlim", &pdata->usb.vlim);
if (ret) {
pdata->usb.vlim = 4500;
pr_err("Failed to read node of upm,upm6910,usb-vlim\n");
}
upm->chg_en_gpio = of_get_named_gpio(np, "upm,chg-en-gpio", 0);
if (upm->chg_en_gpio < 0)
pr_err("upm, chg-en-gpio is not available\n");
gpio_direction_output(upm->chg_en_gpio, 0);
ret = of_property_read_u32(np, "upm,upm6910,usb-ilim", &pdata->usb.ilim);
if (ret) {
pdata->usb.ilim = 2000;
pr_err("Failed to read node of upm,upm6910,usb-ilim\n");
}
ret = of_property_read_u32(np, "upm,upm6910,usb-vreg", &pdata->usb.vreg);
if (ret) {
pdata->usb.vreg = 4200;
pr_err("Failed to read node of upm,upm6910,usb-vreg\n");
}
ret = of_property_read_u32(np, "upm,upm6910,usb-ichg", &pdata->usb.ichg);
if (ret) {
pdata->usb.ichg = 2000;
pr_err("Failed to read node of upm,upm6910,usb-ichg\n");
}
ret = of_property_read_u32(np, "upm,upm6910,stat-pin-ctrl",
&pdata->statctrl);
if (ret) {
pdata->statctrl = 0;
pr_err("Failed to read node of upm,upm6910,stat-pin-ctrl\n");
}
ret = of_property_read_u32(np, "upm,upm6910,precharge-current",
&pdata->iprechg);
if (ret) {
pdata->iprechg = 180;
pr_err("Failed to read node of upm,upm6910,precharge-current\n");
}
ret = of_property_read_u32(np, "upm,upm6910,termination-current",
&pdata->iterm);
if (ret) {
pdata->iterm = 180;
pr_err
("Failed to read node of upm,upm6910,termination-current\n");
}
ret =
of_property_read_u32(np, "upm,upm6910,boost-voltage",
&pdata->boostv);
if (ret) {
pdata->boostv = 5000;
pr_err("Failed to read node of upm,upm6910,boost-voltage\n");
}
ret =
of_property_read_u32(np, "upm,upm6910,boost-current",
&pdata->boosti);
if (ret) {
pdata->boosti = 1200;
pr_err("Failed to read node of upm,upm6910,boost-current\n");
}
ret = of_property_read_u32(np, "upm,upm6910,vac-ovp-threshold",
&pdata->vac_ovp);
if (ret) {
pdata->vac_ovp = 6500;
pr_err("Failed to read node of upm,upm6910,vac-ovp-threshold\n");
}
return pdata;
}
static bool is_hiz_mode_trigger_interrupt(struct upm6910 *upm)
{
bool exist = false;
int ret = 0, vbus = 0, hiz_en = 0;
u8 reg_val = 0, vbus_stat = 0;
union power_supply_propval propval;
if (!upm->usb_psy) {
upm->usb_psy = power_supply_get_by_name("battery");
if (!upm->usb_psy) {
pr_err("%s Couldn't get usb psy\n", __func__);
return -ENODEV;
}
}
ret = power_supply_get_property(upm->usb_psy,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &propval);
if (!ret) {
vbus = propval.intval;
}
ret = upm6910_read_byte(upm, UPM6910_REG_08, &reg_val);
if (!ret) {
vbus_stat = (reg_val & REG08_VBUS_STAT_MASK);
vbus_stat >>= REG08_VBUS_STAT_SHIFT;
}
ret = upm6910_read_byte(upm, UPM6910_REG_00, &reg_val);
if (!ret) {
hiz_en = !!(reg_val & REG00_ENHIZ_MASK);
}
if (vbus > 4000 && vbus_stat != REG08_VBUS_TYPE_OTG && hiz_en) {
exist = true;
}
return exist;
}
static int upm6910_chg_set_usbsw(struct upm6910 *upm,
enum upm6910_usbsw usbsw)
{
struct phy *phy;
int ret, mode = (usbsw == USBSW_CHG) ? PHY_MODE_BC11_SET :
PHY_MODE_BC11_CLR;
pr_info("%s usbsw=%d\n", __func__, usbsw);
phy = phy_get(upm->dev, "usb2-phy");
if (IS_ERR_OR_NULL(phy)) {
pr_err("%s failed to get usb2-phy\n", __func__);
return -ENODEV;
}
ret = phy_set_mode_ext(phy, PHY_MODE_USB_DEVICE, mode);
if (ret)
pr_err("%s failed to set phy ext mode\n", __func__);
phy_put(upm->dev, phy);
return ret;
}
static int upm6910_get_charger_type(struct upm6910 *upm, int *type)
{
int ret;
u8 reg_val = 0;
int vbus_stat = 0;
int chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
ret = upm6910_read_byte(upm, UPM6910_REG_08, &reg_val);
if (ret)
return ret;
vbus_stat = (reg_val & REG08_VBUS_STAT_MASK);
vbus_stat >>= REG08_VBUS_STAT_SHIFT;
pr_info("[%s] reg08: 0x%02x, vbus_stat:%d\n", __func__, reg_val, vbus_stat);
switch (vbus_stat) {
case REG08_VBUS_TYPE_NONE:
chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
upm->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
break;
case REG08_VBUS_TYPE_SDP:
chg_type = POWER_SUPPLY_USB_TYPE_SDP;
upm->psy_desc.type = POWER_SUPPLY_TYPE_USB;
break;
case REG08_VBUS_TYPE_CDP:
chg_type = POWER_SUPPLY_USB_TYPE_CDP;
upm->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
break;
case REG08_VBUS_TYPE_DCP:
case REG08__VBUS_TYPE_HVDCP:
chg_type = POWER_SUPPLY_USB_TYPE_DCP;
upm->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
break;
case REG08_VBUS_TYPE_UNKNOWN:
chg_type = POWER_SUPPLY_USB_TYPE_DCP;
upm->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
break;
case REG08_VBUS_TYPE_NON_STD:
chg_type = POWER_SUPPLY_USB_TYPE_DCP;
upm->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
break;
default:
chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
upm->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
break;
}
//if (chg_type == POWER_SUPPLY_USB_TYPE_SDP || chg_type == POWER_SUPPLY_USB_TYPE_CDP)
upm6910_chg_set_usbsw(upm, USBSW_USB);
*type = chg_type;
return 0;
}
/*prize liuyong, modify for kpoc charging, 20230410, start*/
static void upm6910_inform_kpoc_dwork_handler(struct work_struct *work)
{
struct upm6910 *upm = container_of(work, struct upm6910,
kpoc_dwork.work);
pr_err("%s supply changed\n", __func__);
schedule_delayed_work(&upm->psy_dwork, 0);
power_supply_changed(upm->psy);
}
/*prize liuyong, modify for kpoc charging, 20230410, end*/
static void upm6910_inform_psy_dwork_handler(struct work_struct *work)
{
int ret = 0;
union power_supply_propval propval;
struct upm6910 *upm = container_of(work, struct upm6910,
psy_dwork.work);
if (!upm->psy) {
upm->psy = power_supply_get_by_name("charger");
if (!upm->psy) {
pr_err("%s Couldn't get psy\n", __func__);
mod_delayed_work(system_wq, &upm->psy_dwork,
msecs_to_jiffies(2*1000));
return;
}
}
if (upm->psy_usb_type != POWER_SUPPLY_USB_TYPE_UNKNOWN)
propval.intval = 1;
else
propval.intval = 0;
ret = power_supply_set_property(upm->psy, POWER_SUPPLY_PROP_ONLINE,
&propval);
if (ret < 0)
pr_notice("inform power supply online failed:%d\n", ret);
propval.intval = upm->psy_usb_type;
ret = power_supply_set_property(upm->psy,
POWER_SUPPLY_PROP_CHARGE_TYPE,
&propval);
if (ret < 0)
pr_notice("inform power supply charge type failed:%d\n", ret);
return;
}
static irqreturn_t upm6910_irq_handler(int irq, void *data)
{
int ret;
u8 reg_val;
bool prev_pg;
bool prev_vbus_gd;
bool hiz_irq = false;
struct upm6910 *upm = (struct upm6910 *)data;
ret = upm6910_read_byte(upm, UPM6910_REG_0A, &reg_val);
if (ret)
return IRQ_HANDLED;
prev_vbus_gd = upm->vbus_gd;
upm->vbus_gd = !!(reg_val & REG0A_VBUS_GD_MASK);
ret = upm6910_read_byte(upm, UPM6910_REG_08, &reg_val);
if (ret)
return IRQ_HANDLED;
prev_pg = upm->power_good;
upm->power_good = !!(reg_val & REG08_PG_STAT_MASK);
/*prize liuyong, modify for kpoc charging, 20230410, start*/
if ((upm->bootmode == 8) || (upm->bootmode == 9)) {
cancel_delayed_work_sync(&upm->kpoc_dwork);
}
/*prize liuyong, modify for kpoc charging, 20230410, end*/
if (!prev_vbus_gd && upm->vbus_gd) {
pr_notice("adapter/usb inserted\n");
upm6910_chg_set_usbsw(upm, USBSW_CHG);
} else if (prev_vbus_gd && !upm->vbus_gd) {
hiz_irq = is_hiz_mode_trigger_interrupt(upm);
if (hiz_irq) {
pr_notice("hiz mode trigger interrupt\n");
upm->vbus_gd = true;
upm->power_good = true;
return IRQ_HANDLED;
}
/*prize liuyong, modify for kpoc charging, 20230410, start*/
if ((upm->bootmode == 8) || (upm->bootmode == 9)) {
schedule_delayed_work(&upm->kpoc_dwork,
msecs_to_jiffies(KPOC_CHECK_DELAY));
upm->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
upm->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
pr_notice("adapter/usb removed\n");
return IRQ_HANDLED;
}
/*prize liuyong, modify for kpoc charging, 20230410, end*/
upm->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
upm->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
schedule_delayed_work(&upm->psy_dwork, 0);
upm6910_chg_set_usbsw(upm, USBSW_USB);
pr_notice("adapter/usb removed\n");
//return IRQ_HANDLED;
}
if (!prev_pg && upm->power_good) {
ret = upm6910_get_charger_type(upm, &upm->psy_usb_type);
schedule_delayed_work(&upm->psy_dwork, 0);
}
power_supply_changed(upm->psy);
return IRQ_HANDLED;
}
static int upm6910_register_interrupt(struct upm6910 *upm)
{
int ret = 0;
ret = devm_gpio_request_one(upm->dev, upm->intr_gpio, GPIOF_DIR_IN,
devm_kasprintf(upm->dev, GFP_KERNEL,
"upm6910_intr_gpio.%s", dev_name(upm->dev)));
if (ret < 0) {
pr_err("%s gpio request fail(%d)\n",
__func__, ret);
return ret;
}
upm->client->irq = gpio_to_irq(upm->intr_gpio);
if (upm->client->irq < 0) {
pr_err("%s gpio2irq fail(%d)\n",
__func__, upm->client->irq);
return upm->client->irq;
}
pr_info("%s irq = %d\n", __func__, upm->client->irq);
/* prize liuyong add for charger wakeup, 20240221, start */
ret = devm_request_threaded_irq(upm->dev, upm->client->irq, NULL,
upm6910_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"upm_irq", upm);
/* prize liuyong add for charger wakeup, 20240221, end */
if (ret < 0) {
pr_err("request thread irq failed:%d\n", ret);
return ret;
}
/* prize liuyong add for charger wakeup, 20240221, start */
device_init_wakeup(upm->dev, true);
enable_irq_wake(upm->client->irq);
/* prize liuyong add for charger wakeup, 20240221, end */
return 0;
}
static int upm6910_init_device(struct upm6910 *upm)
{
int ret;
upm6910_disable_watchdog_timer(upm);
upm->input_current_max = 3000;
ret = upm6910_set_stat_ctrl(upm, upm->platform_data->statctrl);
if (ret)
pr_err("Failed to set stat pin control mode, ret = %d\n", ret);
ret = upm6910_set_prechg_current(upm, upm->platform_data->iprechg);
if (ret)
pr_err("Failed to set prechg current, ret = %d\n", ret);
ret = upm6910_set_term_current(upm, upm->platform_data->iterm);
if (ret)
pr_err("Failed to set termination current, ret = %d\n", ret);
ret = upm6910_set_boost_voltage(upm, upm->platform_data->boostv);
if (ret)
pr_err("Failed to set boost voltage, ret = %d\n", ret);
ret = upm6910_set_boost_current(upm, upm->platform_data->boosti);
if (ret)
pr_err("Failed to set boost current, ret = %d\n", ret);
ret = upm6910_set_acovp_threshold(upm, upm->platform_data->vac_ovp);
if (ret)
pr_err("Failed to set acovp threshold, ret = %d\n", ret);
ret = upm6910_set_int_mask(upm,
REG0A_IINDPM_INT_MASK |
REG0A_VINDPM_INT_MASK);
if (ret)
pr_err("Failed to set vindpm and iindpm int mask\n");
return 0;
}
static bool is_usb_rdy(struct device *dev)
{
bool ready = true;
struct device_node *node;
node = of_parse_phandle(dev->of_node, "ssusb", 0);
if (node) {
ready = !of_property_read_bool(node, "cdp-block");
pr_err("usb ready = %d\n", ready);
} else
pr_err("usb node missing or invalid\n");
return ready;
}
/* prize liuyong remove for charger init, 20231129, start*/
#if 0
static void upm6910_inform_prob_dwork_handler(struct work_struct *work)
{
struct upm6910 *upm = container_of(work, struct upm6910,
prob_dwork.work);
int i;
static const int max_wait_cnt = 250;
/* CDP port specific process */
pr_err("force dpdm start\n");
for (i = 0; i < max_wait_cnt; i++) {
if (is_usb_rdy(upm->dev))
break;
msleep(100);
}
if (i == max_wait_cnt)
pr_err("wait usb timeout\n", __func__);
else
pr_err("wait usb free\n", __func__);
msleep(100);
upm6910_dpdm_set_hidden_mode(upm);
/* prize liuyong modify for charger detect, 20231128, start*/
//msleep(700);
//upm6910_force_dpdm(upm);
//msleep(300);
//upm6910_irq_handler(upm->irq, (void *) upm);
/* prize liuyong modify for charger detect, 20231128, end*/
}
#endif
/* prize liuyong remove for charger init, 20231129, end*/
/* prize liuyong add for charger init, 20231206, start*/
static void upm6910_inform_prob_dwork_handler(struct work_struct *work)
{
struct upm6910 *upm = container_of(work, struct upm6910,
prob_dwork.work);
union power_supply_propval prop;
static const int max_wait_cnt = 250;
int i = 0;
int vbus = 0;
int ret = 0;
upm->batt_psy = power_supply_get_by_name("battery");
if (IS_ERR_OR_NULL(upm->batt_psy)) {
pr_err("%s Couldn't get batt_psy\n", __func__);
mod_delayed_work(system_wq, &upm->prob_dwork,
msecs_to_jiffies(2000));
}
if (!IS_ERR_OR_NULL(upm->batt_psy)) {
ret = power_supply_get_property(upm->batt_psy,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, &prop);
vbus = prop.intval;
chr_err("vbus:%s:%d\n", __func__, vbus);
}
if (vbus >= 2500 && upm->psy_usb_type == POWER_SUPPLY_USB_TYPE_UNKNOWN) {
/* CDP port specific process */
pr_err("force dpdm start\n");
for (i = 0; i < max_wait_cnt; i++) {
/* prize liuyong, add for boot up otg state,20240229 start */
if (upm->is_otg_en == true) {
pr_err("%s, exit\n", __func__);
return;
}
/* prize liuyong, add for boot up otg state,20240229 end */
if (is_usb_rdy(upm->dev))
break;
msleep(100);
}
if (i == max_wait_cnt)
pr_err("wait usb timeout\n", __func__);
else
pr_err("wait usb free\n", __func__);
/* prize liuyong ,modify for boot up charger detect error, 20240226, start */
if (upm->bootmode == 8 || upm->bootmode == 9) {
upm6910_chg_set_usbsw(upm, USBSW_CHG);
}
/* prize liuyong ,modify for boot up charger detect error, 20240226, end */
upm6910_force_dpdm(upm);
msleep(200);
upm6910_irq_handler(upm->irq, (void *) upm);
msleep(50);
usb_set_role(1);
}
}
/* prize liuyong add for charger init, 20231206, end*/
static int upm6910_detect_device(struct upm6910 *upm)
{
int ret;
u8 data;
ret = upm6910_read_byte(upm, UPM6910_REG_0B, &data);
if (!ret) {
upm->part_no = (data & REG0B_PN_MASK) >> REG0B_PN_SHIFT;
upm->revision =
(data & REG0B_DEV_REV_MASK) >> REG0B_DEV_REV_SHIFT;
}
return ret;
}
static void upm6910_dump_regs(struct upm6910 *upm)
{
int addr;
u8 val;
int ret;
for (addr = 0x0; addr <= 0x0C; addr++) {
ret = upm6910_read_byte(upm, addr, &val);
if (ret == 0)
pr_err("Reg[%.2x] = 0x%.2x\n", addr, val);
}
}
static ssize_t
upm6910_show_registers(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct upm6910 *upm = dev_get_drvdata(dev);
u8 addr;
u8 val;
u8 tmpbuf[200];
int len;
int idx = 0;
int ret;
idx = snprintf(buf, PAGE_SIZE, "%s:\n", "upm6910 Reg");
for (addr = 0x0; addr <= 0x0B; addr++) {
ret = upm6910_read_byte(upm, addr, &val);
if (ret == 0) {
len = snprintf(tmpbuf, PAGE_SIZE - idx,
"Reg[%.2x] = 0x%.2x\n", addr, val);
memcpy(&buf[idx], tmpbuf, len);
idx += len;
}
}
return idx;
}
static ssize_t
upm6910_store_registers(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct upm6910 *upm = dev_get_drvdata(dev);
int ret;
unsigned int reg;
unsigned int val;
ret = sscanf(buf, "%x %x", &reg, &val);
if (ret == 2 && reg < 0x0B) {
upm6910_write_byte(upm, (unsigned char) reg,
(unsigned char) val);
}
return count;
}
static DEVICE_ATTR(registers, S_IRUGO | S_IWUSR, upm6910_show_registers,
upm6910_store_registers);
static struct attribute *upm6910_attributes[] = {
&dev_attr_registers.attr,
NULL,
};
static const struct attribute_group upm6910_attr_group = {
.attrs = upm6910_attributes,
};
static int upm6910_charging(struct charger_device *chg_dev, bool enable)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret = 0;
u8 val;
if (enable)
ret = upm6910_enable_charger(upm);
else
ret = upm6910_disable_charger(upm);
pr_err("%s charger %s\n", enable ? "enable" : "disable",
!ret ? "successfully" : "failed");
ret = upm6910_read_byte(upm, UPM6910_REG_01, &val);
if (!ret)
upm->charge_enabled = !!(val & REG01_CHG_CONFIG_MASK);
return ret;
}
static int upm6910_plug_in(struct charger_device *chg_dev)
{
int ret;
ret = upm6910_charging(chg_dev, true);
if (ret)
pr_err("Failed to enable charging:%d\n", ret);
return ret;
}
static int upm6910_plug_out(struct charger_device *chg_dev)
{
int ret;
ret = upm6910_charging(chg_dev, false);
if (ret)
pr_err("Failed to disable charging:%d\n", ret);
return ret;
}
static int upm6910_dump_register(struct charger_device *chg_dev)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
upm6910_dump_regs(upm);
return 0;
}
static int upm6910_get_charging_state(struct upm6910 *upm, int *state)
{
//struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret;
u8 val;
u8 otg_state;
u8 chg_fault = 0;
ret = upm6910_read_byte(upm, UPM6910_REG_09, &chg_fault);
if (!ret) {
chg_fault = chg_fault & REG09_FAULT_CHRG_MASK;
chg_fault = chg_fault >> REG09_FAULT_CHRG_SHIFT;
}
ret = upm6910_read_byte(upm, UPM6910_REG_01, &otg_state);
if (!ret) {
otg_state = otg_state & REG01_OTG_CONFIG_MASK;
otg_state = otg_state >> REG01_OTG_CONFIG_SHIFT;
if (otg_state == REG01_OTG_ENABLE) {
*state = POWER_SUPPLY_STATUS_DISCHARGING;
chr_err("%s: otg_state:%d\n", __func__, otg_state);
return ret;
}
}
ret = upm6910_read_byte(upm, UPM6910_REG_08, &val);
if (!ret) {
chr_err("%s:val:%d, chg_fault:%d, en:%d\n", __func__,
val, chg_fault, upm->charge_enabled);
val = val & REG08_CHRG_STAT_MASK;
val = val >> REG08_CHRG_STAT_SHIFT;
switch (val) {
case REG08_CHRG_STAT_IDLE:
case REG08_CHRG_STAT_PRECHG:
case REG08_CHRG_STAT_FASTCHG:
if (upm->charge_enabled)
*state = POWER_SUPPLY_STATUS_CHARGING;
else
*state = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case REG08_CHRG_STAT_CHGDONE:
*state = POWER_SUPPLY_STATUS_FULL;
break;
default:
*state = POWER_SUPPLY_STATUS_UNKNOWN;
break;
}
switch (chg_fault) {
case REG09_FAULT_CHRG_INPUT:
case REG09_FAULT_CHRG_THERMAL:
case REG09_FAULT_CHRG_TIMER:
*state = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
default:
break;
}
} else {
if (upm->charge_enabled)
*state = POWER_SUPPLY_STATUS_CHARGING;
else
*state = POWER_SUPPLY_STATUS_NOT_CHARGING;
chr_err("%s: chg_state:%d\n", __func__, upm->charge_enabled);
}
return ret;
}
static int upm6910_is_charging_enable(struct charger_device *chg_dev, bool *en)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
*en = upm->charge_enabled;
return 0;
}
static int upm6910_is_charging_done(struct charger_device *chg_dev, bool *done)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret;
u8 val;
ret = upm6910_read_byte(upm, UPM6910_REG_08, &val);
if (!ret) {
val = val & REG08_CHRG_STAT_MASK;
val = val >> REG08_CHRG_STAT_SHIFT;
*done = (val == REG08_CHRG_STAT_CHGDONE);
}
return ret;
}
static int upm6910_set_ichg(struct charger_device *chg_dev, u32 curr)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
pr_err("charge curr = %d\n", curr);
return upm6910_set_chargecurrent(upm, curr / 1000);
}
static int upm6910_get_min_ichg(struct charger_device *chg_dev, u32 *curr)
{
*curr = 60 * 1000;
return 0;
}
static int upm6910_set_vchg(struct charger_device *chg_dev, u32 volt)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
pr_err("charge volt = %d\n", volt);
return upm6910_set_chargevolt(upm, volt / 1000);
}
static int upm6910_get_vchg(struct charger_device *chg_dev, u32 *volt)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
u8 reg_val;
int vchg;
int ret;
ret = upm6910_read_byte(upm, UPM6910_REG_04, &reg_val);
if (!ret) {
vchg = (reg_val & REG04_VREG_MASK) >> REG04_VREG_SHIFT;
vchg = vchg * REG04_VREG_LSB + REG04_VREG_BASE;
*volt = vchg * 1000;
}
return ret;
}
static int upm6910_set_ivl(struct charger_device *chg_dev, u32 volt)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
pr_err("vindpm volt = %d\n", volt);
return upm6910_set_input_volt_limit(upm, volt / 1000);
}
static int upm6910_set_icl(struct charger_device *chg_dev, u32 curr)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
pr_err("indpm curr = %d\n", curr);
return upm6910_set_input_current_limit(upm, curr / 1000);
}
static int upm6910_kick_wdt(struct charger_device *chg_dev)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
return upm6910_reset_watchdog_timer(upm);
}
static int upm6910_set_otg(struct charger_device *chg_dev, bool en)
{
int ret;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
if (en)
ret = upm6910_enable_otg(upm);
else
ret = upm6910_disable_otg(upm);
/* prize liuyong, add for boot up otg state,20240229 start */
if (!ret)
upm->is_otg_en = en;
if (en) {
upm6910_set_dp_voltage(upm, REG0C_DPDM_OUT_HIZ);
upm6910_set_dm_voltage(upm, REG0C_DPDM_OUT_HIZ);
}
/* prize liuyong, add for boot up otg state,20240229 end */
pr_err("%s OTG %s\n", en ? "enable" : "disable",
!ret ? "successfully" : "failed");
return ret;
}
static int upm6910_set_safety_timer(struct charger_device *chg_dev, bool en)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret;
if (en)
ret = upm6910_enable_safety_timer(upm);
else
ret = upm6910_disable_safety_timer(upm);
return ret;
}
static int upm6910_is_safety_timer_enabled(struct charger_device *chg_dev,
bool *en)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret;
u8 reg_val;
ret = upm6910_read_byte(upm, UPM6910_REG_05, &reg_val);
if (!ret)
*en = !!(reg_val & REG05_EN_TIMER_MASK);
return ret;
}
static int upm6910_set_boost_ilmt(struct charger_device *chg_dev, u32 curr)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
int ret;
pr_err("otg curr = %d\n", curr);
ret = upm6910_set_boost_current(upm, curr / 1000);
return ret;
}
static int upm6910_get_vbus_voltage(struct charger_device *chg_dev, u32 *vbus)
{
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
static struct power_supply *batt_psy;
union power_supply_propval prop;
int ret = 0;
if(batt_psy == NULL)
batt_psy = power_supply_get_by_name("battery");
if (IS_ERR_OR_NULL(batt_psy)) {
pr_err("%s Couldn't get batt_psy\n", __func__);
if (upm->bootmode == 8 || upm->bootmode == 9) {
*vbus = 5000 * 1000;
pr_err("%s wait batt_psy, rety\n", __func__);
}
} else {
ret = power_supply_get_property(batt_psy,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, &prop);
*vbus = prop.intval * 1000;
chr_err("vbus:%s:%d\n", __func__, *vbus);
}
return ret;
}
static int upm6910_en_pe_current_partern(struct charger_device *chg_dev, bool is_increase)
{
int ret = 0;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
printk("upm6910_en_pe_current_partern start4\n");
mutex_lock(&upm->pe_access_lock);
upm6910_set_icl(upm->chg_dev, 600000);
upm6910_set_ichg(upm->chg_dev, 1000000);
mdelay(80);
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(70);
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50);//50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //1
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 600000); //1
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //stop
mdelay(150);
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(30);//100
upm6910_set_icl(upm->chg_dev, 600000); //
mutex_unlock(&upm->pe_access_lock);
printk("upm6910_en_pe_current_partern end\n");
return ret;
}
static int upm6910_set_pe20_efficiency_table(struct charger_device *chg_dev)
{
return 0;
}
static void upm6910_chg_force_dpdm_work_func(struct work_struct *work)
{
struct upm6910 *upm = container_of(work,
struct upm6910, force_dpdm_work);
int i;
/* prize liuyong ,modify for boot up charger detect error, 20240226, start */
static const int max_wait_cnt = 350;
static bool is_first_detect = true;
/* prize liuyong ,modify for boot up charger detect error, 20240226, end */
/* CDP port specific process */
pr_err("check CDP block\n");
for (i = 0; i < max_wait_cnt; i++) {
if (is_usb_rdy(upm->dev))
break;
msleep(50);
}
if (i == max_wait_cnt)
pr_err("CDP timeout\n", __func__);
else
pr_err("CDP free\n", __func__);
/* prize liuyong ,modify for boot up charger detect error, 20240226, start */
if (is_first_detect == true) {
if (upm->psy_usb_type == POWER_SUPPLY_USB_TYPE_UNKNOWN)
upm6910_chg_set_usbsw(upm, USBSW_CHG);
is_first_detect = false;
}
/* prize liuyong ,modify for boot up charger detect error, 20240226, end */
upm6910_force_dpdm(upm);
return;
}
static int upm6910_en_pe20_current_partern(struct charger_device *chg_dev,u32 is_up)
{
int ret = 0;
struct upm6910 *upm = dev_get_drvdata(&chg_dev->dev);
printk("upm6910_en_pe20_current_partern start4\n");
mutex_lock(&upm->pe_access_lock);
upm6910_set_icl(upm->chg_dev, 600000);
upm6910_set_ichg(upm->chg_dev, 1000000);
mdelay(80);
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(70);
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50);//50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //1
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 600000); //1
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 600000); //0
mdelay(50); //50
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(100); //100
upm6910_set_icl(upm->chg_dev, 600000); //stop
mdelay(150);
upm6910_set_icl(upm->chg_dev, 100000);
mdelay(30);//100
upm6910_set_icl(upm->chg_dev, 600000); //
mutex_unlock(&upm->pe_access_lock);
printk("upm6910_en_pe20_current_partern end\n");
return ret;
}
static int upm6910_set_pe20_reset(struct charger_device *chg_dev)
{
return 0;
}
static int upm6910_enable_cable_drop_comp(struct charger_device *chg_dev, bool en)
{
return 0;
}
static enum power_supply_usb_type upm6910_chg_psy_usb_types[] = {
POWER_SUPPLY_USB_TYPE_UNKNOWN,
POWER_SUPPLY_USB_TYPE_SDP,
POWER_SUPPLY_USB_TYPE_CDP,
POWER_SUPPLY_USB_TYPE_DCP,
};
static enum power_supply_property upm6910_chg_psy_properties[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
};
static int upm6910_chg_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
case POWER_SUPPLY_PROP_STATUS:
case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_ENERGY_EMPTY:
return 1;
default:
return 0;
}
return 0;
}
static int upm6910_chg_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret = 0;
u32 _val;
int data;
struct upm6910 *upm = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "XinHeChip";
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = upm->power_good;
//val->intval = upm->vbus_good;
break;
case POWER_SUPPLY_PROP_STATUS:
ret = upm6910_get_charging_state(upm, &data);
if (ret < 0)
break;
val->intval = data;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
val->intval = upm->constant_chargevolt * 1000;
//ret = upm6910_get_chargevol(upm, &data);
//if (ret < 0)
//break;
//val->intval = data;
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
//ret = upm6910_get_input_current_limit(upm, &_val);
//if (ret < 0)
// break;
val->intval = upm->input_current_limit * 1000;
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
ret = upm6910_get_input_volt_limit(upm, &_val);
if (ret < 0)
break;
val->intval = _val;
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
//ret = upm6910_get_term_current(upm, &data);
//if (ret < 0)
// break;
val->intval = upm->term_current * 1000;
break;
case POWER_SUPPLY_PROP_USB_TYPE:
pr_err("Main--->POWER_SUPPLY_PROP_USB_TYPE : %d\n", upm->psy_usb_type);
val->intval = upm->psy_usb_type;
/* prize liuyong, add for fac test, 20231024, start */
if(upm->bootmode == 1 || upm->bootmode == 4){
pr_err("[%s][%d]type:%d\n",__func__,__LINE__, val->intval);
if(val->intval == 0){
usb_set_role(0);
} //else {
//usb_set_role(1);
//}
}
/* prize liuyong, add for fac test, 20231024, end */
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
if (upm->psy_desc.type == POWER_SUPPLY_TYPE_USB)
val->intval = 500000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
if (upm->psy_desc.type == POWER_SUPPLY_TYPE_USB)
val->intval = 5000000;
break;
case POWER_SUPPLY_PROP_TYPE:
pr_err("---->POWER_SUPPLY_PROP_TYPE : %d\n", upm->psy_desc.type);
val->intval = upm->psy_desc.type;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int upm6910_chg_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
int ret = 0;
struct upm6910 *upm = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
queue_work(upm->wq, &upm->force_dpdm_work);
break;
case POWER_SUPPLY_PROP_STATUS:
if (val->intval) {
ret = upm6910_enable_charger(upm);
} else {
ret = upm6910_disable_charger(upm);
}
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = upm6910_set_chargecurrent(upm, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
upm->constant_chargevolt = val->intval / 1000;
ret = upm6910_set_chargevolt(upm, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
upm->input_current_limit = val->intval / 1000;
ret = upm6910_set_input_current_limit(upm, upm->input_current_limit);
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
ret = upm6910_set_input_volt_limit(upm, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
upm->term_current = val->intval / 1000;
ret = upm6910_set_term_current(upm, val->intval / 1000);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static char *upm6910_psy_supplied_to[] = {
"battery",
"mtk-master-charger",
};
static const struct power_supply_desc upm6910_psy_desc = {
.type = POWER_SUPPLY_TYPE_USB,
.usb_types = upm6910_chg_psy_usb_types,
.num_usb_types = ARRAY_SIZE(upm6910_chg_psy_usb_types),
.properties = upm6910_chg_psy_properties,
.num_properties = ARRAY_SIZE(upm6910_chg_psy_properties),
.property_is_writeable = upm6910_chg_property_is_writeable,
.get_property = upm6910_chg_get_property,
.set_property = upm6910_chg_set_property,
};
static int upm6910_chg_init_psy(struct upm6910 *upm)
{
struct power_supply_config cfg = {
.drv_data = upm,
.of_node = upm->dev->of_node,
.supplied_to = upm6910_psy_supplied_to,
.num_supplicants = ARRAY_SIZE(upm6910_psy_supplied_to),
};
memcpy(&upm->psy_desc, &upm6910_psy_desc, sizeof(upm->psy_desc));
upm->psy_desc.name = "primary_chg";//dev_name(upm->dev);
upm->psy = devm_power_supply_register(upm->dev, &upm->psy_desc,&cfg);
//upm->pdpe_psy = power_supply_get_by_name("pdpe-state");
//if(upm->pdpe_psy == NULL){
// pr_err("gezi get info->pdpe_psy failed\n");
//}
if (!upm->chg_psy) {
upm->chg_psy = power_supply_get_by_name("mtk-master-charger");
}
return IS_ERR(upm->psy) ? PTR_ERR(upm->psy) : 0;
}
static struct charger_ops upm6910_chg_ops = {
/* Normal charging */
.plug_in = upm6910_plug_in,
.plug_out = upm6910_plug_out,
.dump_registers = upm6910_dump_register,
.enable = upm6910_charging,
.is_enabled = upm6910_is_charging_enable,
.get_charging_current = upm6910_get_ichg,
.set_charging_current = upm6910_set_ichg,
.get_input_current = upm6910_get_icl,
.set_input_current = upm6910_set_icl,
.get_constant_voltage = upm6910_get_vchg,
.set_constant_voltage = upm6910_set_vchg,
.kick_wdt = upm6910_kick_wdt,
.set_mivr = upm6910_set_ivl,
.is_charging_done = upm6910_is_charging_done,
.get_min_charging_current = upm6910_get_min_ichg,
.enable_termination = upm6910_enable_term,
/* Safety timer */
.enable_safety_timer = upm6910_set_safety_timer,
.is_safety_timer_enabled = upm6910_is_safety_timer_enabled,
/* Power path */
.enable_powerpath = NULL,
.is_powerpath_enabled = NULL,
/* OTG */
.enable_otg = upm6910_set_otg,
.set_boost_current_limit = upm6910_set_boost_ilmt,
.enable_discharge = NULL,
/* PE+/PE+20 */
.send_ta20_current_pattern = upm6910_en_pe20_current_partern,
.send_ta_current_pattern = upm6910_en_pe_current_partern,
.set_pe20_efficiency_table = upm6910_set_pe20_efficiency_table,
.reset_ta = upm6910_set_pe20_reset,
.enable_cable_drop_comp = upm6910_enable_cable_drop_comp,
/* Event */
.event = upm6910_do_event,
/* HIZ */
//.set_hiz_mode = upm6910_set_hiz_mode,
//.get_hiz_mode = upm6910_get_hiz_mode,
.enable_hz = upm6910_set_hiz_mode,
/* ADC */
.get_tchg_adc = NULL,
/* 6pin battery */
.enable_6pin_battery_charging = NULL,
.get_vbus_adc = upm6910_get_vbus_voltage,
};
static struct of_device_id upm6910_charger_match_table[] = {
{
.compatible = "upm,upm6910_charger",
.data = &pn_data[PN_UPM6910D],
},
{},
};
MODULE_DEVICE_TABLE(of, upm6910_charger_match_table);
static int upm6910_charger_remove(struct i2c_client *client);
static int upm6910_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upm6910 *upm;
const struct of_device_id *match;
struct device_node *node = client->dev.of_node;
int ret = 0;
pr_err("upm6910 start\n");
upm = devm_kzalloc(&client->dev, sizeof(struct upm6910), GFP_KERNEL);
if (!upm)
return -ENOMEM;
client->addr = 0x6B;
upm->dev = &client->dev;
upm->client = client;
i2c_set_clientdata(client, upm);
mutex_init(&upm->i2c_rw_lock);
ret = upm6910_detect_device(upm);
if (ret) {
pr_err("No upm6910 device found!\n");
return -ENODEV;
}
match = of_match_node(upm6910_charger_match_table, node);
if (match == NULL) {
pr_err("device tree match not found\n");
return -EINVAL;
}
if (upm->part_no != *(int *)match->data) {
pr_info("part no mismatch, hw:%s, devicetree:%s\n",
pn_str[upm->part_no], pn_str[*(int *) match->data]);
upm6910_charger_remove(client);
return -EINVAL;
}
upm->platform_data = upm6910_parse_dt(node, upm);
if (!upm->platform_data) {
pr_err("No platform data provided.\n");
return -EINVAL;
}
/* prize liuyong, add for boot up otg state,20240229 start */
upm->is_otg_en = false;
/* prize liuyong, add for boot up otg state,20240229 end */
INIT_DELAYED_WORK(&upm->psy_dwork, upm6910_inform_psy_dwork_handler);
/* prize liuyong add for charger init, 20231206, start*/
INIT_DELAYED_WORK(&upm->prob_dwork, upm6910_inform_prob_dwork_handler);
/* prize liuyong add for charger init, 20231206, end*/
/*prize liuyong, modify for kpoc charging, 20230410, start*/
INIT_DELAYED_WORK(&upm->kpoc_dwork, upm6910_inform_kpoc_dwork_handler);
INIT_WORK(&upm->force_dpdm_work, upm6910_chg_force_dpdm_work_func);
/*prize liuyong, modify for kpoc charging, 20230410, end*/
ret = upm6910_chg_init_psy(upm);
if (ret < 0) {
pr_err("failed init power supply\n");
return -EINVAL;
}
ret = upm6910_init_device(upm);
if (ret) {
pr_err("Failed to init device\n");
return ret;
}
upm6910_register_interrupt(upm);
upm->chg_dev = charger_device_register(upm->chg_dev_name,
&client->dev, upm,
&upm6910_chg_ops,
&upm6910_chg_props);
if (IS_ERR_OR_NULL(upm->chg_dev)) {
ret = PTR_ERR(upm->chg_dev);
return ret;
}
if (!upm->usb_psy) {
upm->usb_psy = power_supply_get_by_name("battery");
if (!upm->usb_psy) {
pr_err("%s Couldn't get usb psy\n", __func__);
}
}
ret = sysfs_create_group(&upm->dev->kobj, &upm6910_attr_group);
if (ret)
dev_err(upm->dev, "failed to register sysfs. err: %d\n", ret);
upm->wq = create_singlethread_workqueue(dev_name(upm->dev));
if (!upm->wq) {
pr_err( "failed to create workqueue\n");
ret = -ENOMEM;
return ret;
}
/* prize liuyong add for charger init, 20231206, start*/
upm->batt_psy = power_supply_get_by_name("battery");
if (IS_ERR_OR_NULL(upm->batt_psy)) {
pr_err("%s Couldn't get batt_psy\n", __func__);
}
mod_delayed_work(system_wq, &upm->prob_dwork,
msecs_to_jiffies(4*1000));
/* prize liuyong add for charger init, 20231206, end*/
pr_err("upm6910 probe successfully, Part Num:%d, Revision:%d\n!",
upm->part_no, upm->revision);
return 0;
}
/* prize liuyong add for charger wakeup, 20240221, start */
#ifdef CONFIG_PM
static int upm6910x_suspend(struct device *dev)
{
struct upm6910 *upm = dev_get_drvdata(dev);
dev_info(dev, "%s\n", __func__);
if (device_may_wakeup(dev)) {
enable_irq_wake(upm->client->irq);
}
disable_irq(upm->client->irq);
return 0;
}
static int upm6910x_resume(struct device *dev)
{
struct upm6910 *upm = dev_get_drvdata(dev);
dev_info(dev, "%s\n", __func__);
enable_irq(upm->client->irq);
if (device_may_wakeup(dev)) {
disable_irq_wake(upm->client->irq);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(upm6910x_pm_ops, upm6910x_suspend, upm6910x_resume);
#endif
/* prize liuyong add for charger wakeup, 20240221, end */
static int upm6910_charger_remove(struct i2c_client *client)
{
struct upm6910 *upm = i2c_get_clientdata(client);
mutex_destroy(&upm->i2c_rw_lock);
/* prize liuyong add for charger wakeup, 20240221, start */
device_init_wakeup(upm->dev, 0);
/* prize liuyong add for charger wakeup, 20240221, end */
sysfs_remove_group(&upm->dev->kobj, &upm6910_attr_group);
destroy_workqueue(upm->wq);
return 0;
}
static void upm6910_charger_shutdown(struct i2c_client *client)
{
}
static struct i2c_driver upm6910_charger_driver = {
.driver = {
.name = "upm6910-charger",
.owner = THIS_MODULE,
.of_match_table = upm6910_charger_match_table,
/* prize liuyong add for charger wakeup, 20240221, start */
#ifdef CONFIG_PM
.pm = &upm6910x_pm_ops,
#endif
/* prize liuyong add for charger wakeup, 20240221, end */
},
.probe = upm6910_charger_probe,
.remove = upm6910_charger_remove,
.shutdown = upm6910_charger_shutdown,
};
module_i2c_driver(upm6910_charger_driver);
MODULE_DESCRIPTION("Unisemipower UPM6910D Charger Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Unisemepower");