kernel-brax3-ubuntu-touch/drivers/power/supply/sc854x_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

2161 lines
54 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Southchip Semiconductor Technology(Shanghai) Co., Ltd.
*/
#define pr_fmt(fmt) "[sc854x] %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/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/debugfs.h>
#include <linux/bitops.h>
#include <linux/math64.h>
//#include "mtk_charger_intf.h"
#include "sc854x_reg.h"
#include "mtk_charger.h"
//prize add by lipengpeng 20220913 start
#if IS_ENABLED(CONFIG_FACTORY_CHARGE)&&IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT)
extern int is_chg2_exist;
#endif /* CONFIG_FACTORY_CHARGE CONFIG_SECOND_CHARGER_SUPPORT*/
//prize add by lipengpeng 20220913 end
enum {
SC8545_ID,
SC8546_ID,
SC8547_ID,
SC8549_ID,
};
static int id_data[] = {
[SC8545_ID] = 0x66,
[SC8546_ID] = 0x67,
[SC8547_ID] = 0x66,
[SC8549_ID] = 0x49,//drv add by liuruiqian for chip_id,20240311
};
enum {
SC8545_STANDALONG = 0,
SC8545_MASTER,
SC8545_SLAVE,
SC8546_STANDALONG,
SC8546_MASTER,
SC8546_SLAVE,
SC8547_STANDALONG,
SC8547_MASTER,
SC8547_SLAVE,
SC8549_STANDALONG,
SC8549_MASTER,
SC8549_SLAVE,
};
static int sc854x_mode_data[] = {
[SC8545_STANDALONG] = SC8545_STANDALONG,
[SC8545_MASTER] = SC8545_MASTER,
[SC8545_SLAVE] = SC8545_SLAVE,
[SC8546_STANDALONG] = SC8546_STANDALONG,
[SC8546_MASTER] = SC8546_MASTER,
[SC8546_SLAVE] = SC8546_SLAVE,
[SC8547_STANDALONG] = SC8547_STANDALONG,
[SC8547_MASTER] = SC8547_MASTER,
[SC8547_SLAVE] = SC8547_SLAVE,
[SC8549_STANDALONG] = SC8549_STANDALONG,
[SC8549_MASTER] = SC8549_MASTER,
[SC8549_SLAVE] = SC8549_SLAVE,
};
typedef enum{
STANDALONE = 0,
MASTER,
SLAVE,
}WORK_MODE;
enum SC854X_ADC_CH{
ADC_IBUS,
ADC_VBUS,
ADC_VAC,
ADC_VOUT,
ADC_VBAT,
ADC_IBAT,
ADC_TDIE,
ADC_MAX_NUM,
};
static const u32 sc854x_adc_accuracy_tbl[ADC_MAX_NUM] = {
150000,/* IBUS */
35000,/* VBUS */
35000,/* VAC */
20000,/* VOUT */
20000,/* VBAT */
200000,/* IBAT */
4/* TDIE */
};
#define sc_err(fmt, ...) \
do { \
if (sc->mode == STANDALONE) \
printk(KERN_ERR "[sc854x-STANDALONE]:%s:" fmt, __func__, ##__VA_ARGS__);\
else if (sc->mode == MASTER) \
printk(KERN_ERR "[sc854x-MASTER]:%s:" fmt, __func__, ##__VA_ARGS__);\
else \
printk(KERN_ERR "[sc854x-SLAVE]:%s:" fmt, __func__, ##__VA_ARGS__);\
} while(0);
#define sc_info(fmt, ...) \
do { \
if (sc->mode == STANDALONE) \
printk(KERN_INFO "[sc854x-STANDALONE]:%s:" fmt, __func__, ##__VA_ARGS__);\
else if (sc->mode == MASTER) \
printk(KERN_INFO "[sc854x-MASTER]:%s:" fmt, __func__, ##__VA_ARGS__);\
else \
printk(KERN_INFO "[sc854x-SLAVE]:%s:" fmt, __func__, ##__VA_ARGS__);\
} while(0);
#define sc_dbg(fmt, ...) \
do { \
if (sc->mode == STANDALONE) \
printk(KERN_DEBUG "[sc854x-STANDALONE]:%s:" fmt, __func__, ##__VA_ARGS__);\
else if (sc->mode == MASTER) \
printk(KERN_DEBUG "[sc854x-MASTER]:%s:" fmt, __func__, ##__VA_ARGS__);\
else \
printk(KERN_DEBUG "[sc854x-MASTER]:%s:" fmt, __func__, ##__VA_ARGS__);\
} while(0);
struct sc854x_cfg {
const char *chg_name;
bool bat_ovp_disable;
bool bat_ocp_disable;
bool vdrop_ovp_disable;
int bat_ovp_th;
int bat_ocp_th;
bool bus_ovp_disable;
bool bus_ocp_disable;
bool bus_ucp_disable;
int bus_ovp_th;
int bus_ocp_th;
int ac_ovp_th;
int sense_r_mohm;
};
struct sc854x {
struct device *dev;
struct i2c_client *client;
int mode;
int irq_gpio;
int irq;
struct mutex data_lock;
struct mutex i2c_rw_lock;
struct mutex irq_complete;
bool irq_waiting;
bool irq_disabled;
bool resume_completed;
bool usb_present;
bool charge_enabled; /* Register bit status */
int vbus_error;
/* ADC reading */
int vbat_volt;
int vbus_volt;
int vout_volt;
int vac_volt;
int ibat_curr;
int ibus_curr;
int die_temp;
struct sc854x_cfg *cfg;
int skip_writes;
int skip_reads;
struct sc854x_platform_data *platform_data;
struct charger_device *chg_dev;
struct charger_properties chg_prop;
struct power_supply_desc psy_desc;
struct power_supply_config psy_cfg;
struct power_supply *fc2_psy;
struct power_supply *cw_bat;
struct power_supply *charge_psy;
bool sc_adc_disabled;
};
/************************************************************************/
static int __sc854x_read_word(struct sc854x *sc, u8 reg, u16 *data)
{
s32 ret;
ret = i2c_smbus_read_word_data(sc->client, reg);
if (ret < 0) {
sc_err("i2c read fail: can't read from reg 0x%02X\n", reg);
return ret;
}
*data = (u16) ret;
return 0;
}
static int __sc854x_read_byte(struct sc854x *sc, u8 reg, u8 *data)
{
s32 ret;
ret = i2c_smbus_read_byte_data(sc->client, reg);
if (ret < 0) {
sc_err("i2c read fail: can't read from reg 0x%02X\n", reg);
return ret;
}
*data = (u8) ret;
return 0;
}
static int __sc854x_write_byte(struct sc854x *sc, int reg, u8 val)
{
s32 ret;
ret = i2c_smbus_write_byte_data(sc->client, reg, val);
if (ret < 0) {
sc_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n",
val, reg, ret);
return ret;
}
return 0;
}
static int sc854x_read_byte(struct sc854x *sc, u8 reg, u8 *data)
{
int ret;
if (sc->skip_reads) {
*data = 0;
return 0;
}
mutex_lock(&sc->i2c_rw_lock);
ret = __sc854x_read_byte(sc, reg, data);
mutex_unlock(&sc->i2c_rw_lock);
return ret;
}
static int sc854x_write_byte(struct sc854x *sc, u8 reg, u8 data)
{
int ret;
if (sc->skip_writes)
return 0;
mutex_lock(&sc->i2c_rw_lock);
ret = __sc854x_write_byte(sc, reg, data);
mutex_unlock(&sc->i2c_rw_lock);
return ret;
}
static int sc854x_read_word(struct sc854x *sc, u8 reg, u16 *data)
{
int ret;
if (sc->skip_reads) {
*data = 0;
return 0;
}
mutex_lock(&sc->i2c_rw_lock);
ret = __sc854x_read_word(sc, reg, data);
mutex_unlock(&sc->i2c_rw_lock);
return ret;
}
static int sc854x_update_bits(struct sc854x*sc, u8 reg,
u8 mask, u8 data)
{
int ret;
u8 tmp;
if (sc->skip_reads || sc->skip_writes)
return 0;
mutex_lock(&sc->i2c_rw_lock);
ret = __sc854x_read_byte(sc, reg, &tmp);
if (ret) {
sc_err("Failed: reg=%02X, ret=%d\n", reg, ret);
goto out;
}
tmp &= ~mask;
tmp |= data & mask;
ret = __sc854x_write_byte(sc, reg, tmp);
if (ret)
sc_err("Failed: reg=%02X, ret=%d\n", reg, ret);
out:
mutex_unlock(&sc->i2c_rw_lock);
return ret;
}
/*********************************************************************/
static int sc854x_enable_batovp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_BAT_OVP_ENABLE;
else
val = SC854X_BAT_OVP_DISABLE;
val <<= SC854X_BAT_OVP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_00,
SC854X_BAT_OVP_DIS_MASK, val);
}
static int sc854x_set_batovp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_BAT_OVP_BASE)
threshold = SC854X_BAT_OVP_BASE;
val = (threshold - SC854X_BAT_OVP_BASE) / SC854X_BAT_OVP_LSB;
val <<= SC854X_BAT_OVP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_00,
SC854X_BAT_OVP_MASK, val);
}
static int sc854x_enable_batocp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_BAT_OCP_ENABLE;
else
val = SC854X_BAT_OCP_DISABLE;
val <<= SC854X_BAT_OCP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_01,
SC854X_BAT_OCP_DIS_MASK, val);
}
static int sc854x_set_batocp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_BAT_OCP_BASE)
threshold = SC854X_BAT_OCP_BASE;
val = (threshold - SC854X_BAT_OCP_BASE) / SC854X_BAT_OCP_LSB;
val <<= SC854X_BAT_OCP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_01,
SC854X_BAT_OCP_MASK, val);
}
static int sc854x_set_acovp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_AC_OVP_BASE)
threshold = SC854X_AC_OVP_BASE;
if (threshold == SC854X_AC_OVP_6P5V)
val = 0x07;
else
val = (threshold - SC854X_AC_OVP_BASE) / SC854X_AC_OVP_LSB;
val <<= SC854X_AC_OVP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_02,
SC854X_AC_OVP_MASK, val);
}
static int sc854x_enable_dropovp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_VDROP_OVP_ENABLE;
else
val = SC854X_VDROP_OVP_DISABLE;
val <<= SC854X_VDROP_OVP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_03,
SC854X_VDROP_OVP_DIS_MASK, val);
}
static int sc854x_set_vdrop_ovp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold == 400)
val = SC854X_VDROP_OVP_THRESHOLD_400MV;
else
val = SC854X_VDROP_OVP_THRESHOLD_300MV;
val <<= SC854X_VDROP_OVP_THRESHOLD_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_03,
SC854X_VDROP_OVP_THRESHOLD_MASK, val);
}
static int sc854x_set_vdrop_deglitch(struct sc854x *sc, int deglitch)
{
u8 val;
if (deglitch == 5000)
val = SC854X_VDROP_DEGLITCH_SET_5MS;
else
val = SC854X_VDROP_DEGLITCH_SET_8US;
val <<= SC854X_VDROP_DEGLITCH_SET_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_03,
SC854X_VDROP_DEGLITCH_SET_MASK, val);
}
static int sc854x_enable_busovp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_VBUS_OVP_ENABLE;
else
val = SC854X_VBUS_OVP_DISABLE;
val <<= SC854X_VBUS_OVP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_04,
SC854X_VBUS_OVP_DIS_MASK, val);
}
static int sc854x_set_busovp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_VBUS_OVP_BASE)
threshold = SC854X_VBUS_OVP_BASE;
val = (threshold - SC854X_VBUS_OVP_BASE) / SC854X_VBUS_OVP_LSB;
val <<= SC854X_VBUS_OVP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_04,
SC854X_VBUS_OVP_MASK, val);
}
static int sc854x_enable_busucp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_IBUS_UCP_ENABLE;
else
val = SC854X_IBUS_UCP_DISABLE;
val <<= SC854X_IBUS_UCP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_05,
SC854X_IBUS_UCP_DIS_MASK, val);
}
static int sc854x_enable_busocp(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_IBUS_OCP_ENABLE;
else
val = SC854X_IBUS_OCP_DISABLE;
val <<= SC854X_IBUS_OCP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_05,
SC854X_IBUS_OCP_DIS_MASK, val);
}
static int sc854x_set_busocp_th(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_IBUS_OCP_BASE)
threshold = SC854X_IBUS_OCP_BASE;
val = (threshold - SC854X_IBUS_OCP_BASE) / SC854X_IBUS_OCP_LSB;
val += 1;
val <<= SC854X_IBUS_OCP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_05,
SC854X_IBUS_OCP_MASK, val);
}
static int sc854x_check_vbus_error_status(struct sc854x *sc, int *result)
{
u8 ret;
u8 reg;
ret = sc854x_read_byte(sc, SC854X_REG_06, &reg);
*result = (int)reg;
return ret;
}
static int sc854x_enable_charge(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_CHG_ENABLE;
else
val = SC854X_CHG_DISABLE;
val <<= SC854X_CHG_EN_SHIFT;
sc_err("sc854x charger %s\n", enable == false ? "disable" : "enable");
return sc854x_update_bits(sc, SC854X_REG_07,
SC854X_CHG_EN_MASK, val);
}
static int sc854x_check_charge_enabled(struct sc854x *sc, bool *enabled)
{
int ret;
u8 val;
ret = sc854x_read_byte(sc, SC854X_REG_07, &val);
if (!ret) {
sc_info(">>>reg [0x07] = 0x%02x\n", val);
if (val & SC854X_CHG_EN_MASK) {
ret = sc854x_read_byte(sc, SC854X_REG_06, &val);
if (!ret) {
sc_info(">>>reg [0x06] = 0x%02x\n", val);
if (val & SC854X_CP_SWITCHING_STAT_MASK) {
*enabled = true;
return ret;
}
}
}
}
*enabled = false;
return ret;
}
static int sc854x_reg_reset(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_RESET_REG;
else
val = SC854X_NO_REG_RESET;
val <<= SC854X_REG_RESET_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_07,
SC854X_REG_RESET_MASK, val);
}
static int sc854x_set_ss_timeout(struct sc854x *sc, int timeout)
{
u8 val;
switch (timeout) {
case 0:
val = SC854X_SS_TIMEOUT_DISABLE;
break;
case 40:
val = SC854X_SS_TIMEOUT_40MS;
break;
case 80:
val = SC854X_SS_TIMEOUT_80MS;
break;
case 320:
val = SC854X_SS_TIMEOUT_320MS;
break;
case 1280:
val = SC854X_SS_TIMEOUT_1280MS;
break;
case 5120:
val = SC854X_SS_TIMEOUT_5120MS;
break;
case 20480:
val = SC854X_SS_TIMEOUT_20480MS;
break;
case 81920:
val = SC854X_SS_TIMEOUT_81920MS;
break;
default:
val = SC854X_SS_TIMEOUT_DISABLE;
break;
}
val <<= SC854X_SS_TIMEOUT_SET_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_08,
SC854X_SS_TIMEOUT_SET_MASK,
val);
}
static int sc854x_reg_timeout_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_650MS_REG_TIMEOUT_ENABLE;
else
val = SC854X_650MS_REG_TIMEOUT_DISABLE;
val <<= SC854X_REG_TIMEOUT_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_08,
SC854X_REG_TIMEOUT_DIS_MASK, val);
}
static int sc854x_voutovp_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_VOUT_OVP_ENABLE;
else
val = SC854X_VOUT_OVP_DISABLE;
val <<= SC854X_VOUT_OVP_DIS_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_08,
SC854X_VOUT_OVP_DIS_MASK, val);
}
static int sc854x_set_sense_resistor(struct sc854x *sc, int r_mohm)
{
u8 val;
if (r_mohm == 2)
val = SC854X_SET_IBAT_SNS_RES_2MHM;
else if (r_mohm == 5)
val = SC854X_SET_IBAT_SNS_RES_5MHM;
else
return -EINVAL;
val <<= SC854X_SET_IBAT_SNS_RES_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_08,
SC854X_SET_IBAT_SNS_RES_MASK,
val);
}
static int sc854x_vbus_pd_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_VBUS_PD_ENABLE;
else
val = SC854X_VBUS_PD_DISABLE;
val <<= SC854X_VBUS_PD_EN_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_08,
SC854X_VBUS_PD_EN_MASK, val);
}
/*
static int sc854x_set_charge_mode(struct sc854x *sc, u8 charge_mode)
{
u8 val;
if(charge_mode)
val = SC854X_CHARGE_MODE_1_1;
else
val = SC854X_CHARGE_MODE_2_1;
val <<= SC854X_CHARGE_MODE_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_09,
SC854X_CHARGE_MODE_MASK, val);
}
static int sc854x_get_charge_mode(struct sc854x *sc, int *result)
{
int ret;
u8 val;
ret = sc854x_read_byte(sc, SC854X_REG_09, &val);
if(ret < 0) {
return ret;
}
*result = (val >> SC854X_CHARGE_MODE_SHIFT) &
SC854X_CHARGE_MODE_MASK;
return ret;
}*/
static int sc854x_set_wdt(struct sc854x *sc, int ms)
{
u8 val;
if (ms == 0)
val = SC854X_WATCHDOG_DIS;
else if (ms == 200)
val = SC854X_WATCHDOG_200MS;
else if (ms == 500)
val = SC854X_WATCHDOG_500MS;
else if (ms == 1000)
val = SC854X_WATCHDOG_1S;
else if (ms == 5000)
val = SC854X_WATCHDOG_5S;
else if (ms == 30000)
val = SC854X_WATCHDOG_30S;
else
val = SC854X_WATCHDOG_DIS;
val <<= SC854X_WATCHDOG_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_09,
SC854X_WATCHDOG_MASK, val);
}
static int sc854x_vbat_regulation_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_VBAT_REG_ENABLE;
else
val = SC854X_VBAT_REG_DISABLE;
val <<= SC854X_VBAT_REG_EN_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0A,
SC854X_VBAT_REG_EN_MASK, val);
}
static int sc854x_set_vbat_regulation(struct sc854x *sc, int mv)
{
u8 val;
if (mv == 50)
val = SC854X_SET_VBATREG_50MV;
else if (mv == 100)
val = SC854X_SET_VBATREG_100MV;
else if (mv == 150)
val = SC854X_SET_VBATREG_150MV;
else if (mv == 200)
val = SC854X_SET_VBATREG_200MV;
else
val = SC854X_SET_VBATREG_50MV;
val <<= SC854X_SET_VBATREG_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0A,
SC854X_SET_VBATREG_MASK, val);
}
static int sc854x_ibat_regulation_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_IBAT_REG_ENABLE;
else
val = SC854X_IBAT_REG_DISABLE;
val <<= SC854X_IBAT_REG_EN_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0B,
SC854X_IBAT_REG_EN_MASK, val);
}
static int sc854x_set_ibat_regulation(struct sc854x *sc, int ma)
{
u8 val;
if (ma == 200)
val = SC854X_SET_IBATREG_200MA;
else if (ma == 300)
val = SC854X_SET_IBATREG_300MA;
else if (ma == 450)
val = SC854X_SET_IBATREG_400MA;
else if (ma == 500)
val = SC854X_SET_IBATREG_500MA;
else
val = SC854X_SET_IBATREG_200MA;
val <<= SC854X_SET_VBATREG_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0B,
SC854X_SET_VBATREG_MASK, val);
}
static int sc854x_ibus_regulation_enabled(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_IBUS_REG_ENABLE;
else
val = SC854X_IBUS_REG_DISABLE;
val <<= SC854X_IBUS_REG_EN_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0C,
SC854X_IBUS_REG_EN_MASK, val);
}
static int sc854x_set_ibus_regulation(struct sc854x *sc, int threshold)
{
u8 val;
if (threshold < SC854X_SET_IBUSREG_BASE)
threshold = SC854X_SET_IBUSREG_BASE;
val = (threshold - SC854X_SET_IBUSREG_BASE) / SC854X_SET_IBUSREG_LSB;
val <<= SC854X_SET_IBUSREG_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0C,
SC854X_SET_IBUSREG_MASK, val);
}
static int sc854x_set_pmid2out_uvp(struct sc854x *sc, int mv)
{
u8 val;
if (mv == -50)
val = SC854X_PMID2OUT_UVP_U_50MV;
else if (mv == -100)
val = SC854X_PMID2OUT_UVP_U_100MV;
else if (mv == -150)
val = SC854X_PMID2OUT_UVP_U_150MV;
else if (mv == -200)
val = SC854X_PMID2OUT_UVP_U_200MV;
else
val = SC854X_PMID2OUT_UVP_U_50MV;
val <<= SC854X_PMID2OUT_UVP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0D,
SC854X_PMID2OUT_UVP_MASK, val);
}
static int sc854x_set_pmid2out_ovp(struct sc854x *sc, int mv)
{
u8 val;
if (mv == 200)
val = SC854X_PMID2OUT_OVP_200MV;
else if (mv == 300)
val = SC854X_PMID2OUT_OVP_300MV;
else if (mv == 400)
val = SC854X_PMID2OUT_OVP_400MV;
else if (mv == 500)
val = SC854X_PMID2OUT_OVP_500MV;
else
val = SC854X_PMID2OUT_OVP_200MV;
val <<= SC854X_PMID2OUT_OVP_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_0D,
SC854X_PMID2OUT_OVP_MASK, val);
}
static int sc854x_enable_adc(struct sc854x *sc, bool enable)
{
u8 val;
if (enable)
val = SC854X_ADC_ENABLE;
else
val = SC854X_ADC_DISABLE;
val <<= SC854X_ADC_EN_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_11,
SC854X_ADC_EN_MASK, val);
}
static int sc854x_set_adc_scanrate(struct sc854x *sc, bool oneshot)
{
u8 val;
if (oneshot)
val = SC854X_ADC_RATE_ONESHOT;
else
val = SC854X_ADC_RATE_CONTINOUS;
val <<= SC854X_ADC_RATE_SHIFT;
return sc854x_update_bits(sc, SC854X_REG_11,
SC854X_ADC_EN_MASK, val);
}
static int sc854x_set_adc_scan(struct sc854x *sc, int channel, bool enable)
{
u8 mask;
u8 shift;
u8 val;
if (channel > ADC_MAX_NUM)
return -EINVAL;
shift = SC854X_ADC_DIS_SHIFT - channel;
mask = SC854X_ADC_DIS_MASK << shift;
if (enable)
val = SC854X_ADC_CHANNEL_ENABLE << shift;
else
val = SC854X_ADC_CHANNEL_DISABLE << shift;
return sc854x_update_bits(sc, SC854X_REG_12, mask, val);
}
static int sc854x_get_adc_data(struct sc854x *sc, int channel, int *result)
{
int ret;
u16 val;
u16 t;
union power_supply_propval prop;
if(channel >= ADC_MAX_NUM)return 0;
usleep_range(15000, 17000);
if(channel == ADC_IBAT){
if(!sc->cw_bat){
sc->cw_bat = power_supply_get_by_name("cw-bat");
if(!sc->cw_bat){
sc_err("gezi failed to get cw_bat too!\n");
}
else{
ret = power_supply_get_property(sc->cw_bat,POWER_SUPPLY_PROP_CURRENT_NOW, &prop);
*result = prop.intval;
return 0;
}
}
else{
ret = power_supply_get_property(sc->cw_bat,POWER_SUPPLY_PROP_CURRENT_NOW, &prop);
*result = prop.intval;
return 0;
}
}
ret = sc854x_read_word(sc, SC854X_REG_13 + (channel << 1), &val);
if (ret < 0)
return ret;
t = val & 0xFF;
t <<= 8;
t |= (val >> 8) & 0xFF;
val = t;
switch (channel)
{
case ADC_IBUS:
*result = val * SC854X_IBUS_ADC_LSB;
break;
case ADC_VBUS:
*result = val * SC854X_VBUS_ADC_LSB;
break;
case ADC_VAC:
*result = val * SC854X_VAC_ADC_LSB;
break;
case ADC_VOUT:
*result = val * SC854X_VOUT_ADC_LSB;
break;
case ADC_VBAT:
*result = val * SC854X_VBAT_ADC_LSB;
break;
case ADC_IBAT:
*result = val * SC854X_IBAT_ADC_LSB;
break;
case ADC_TDIE:
*result = val * SC854X_TDIE_ADC_LSB;
break;
default:
sc_err("Not find adc channel : %d\n", channel);
break;
}
sc_err("%s:---->chan:%d, val:%d, result:%d\n", __func__, channel, val, *result);
return 0;
}
static int sc854x_disable_vbus_range(struct sc854x *sc)
{
return sc854x_update_bits(sc, SC854X_REG_3C,
SC854X_VBUS_IN_RANGE_DIS_MASK,
SC854X_VBUS_EN_RANGE_DISABLE << SC854X_VBUS_IN_RANGE_DIS_SHIFT);
}
//*********************************************************************
/*
* interrupt does nothing, just info event chagne, other module could get info
* through power supply interface
*/
static void sc854x_check_fault_status(struct sc854x *sc);
static irqreturn_t sc854x_charger_interrupt(int irq, void *dev_id)
{
struct sc854x *sc = dev_id;
sc_err("INT OCCURED\n");
#if 1
mutex_lock(&sc->irq_complete);
sc->irq_waiting = true;
if (!sc->resume_completed) {
dev_dbg(sc->dev, "IRQ triggered before device-resume\n");
if (!sc->irq_disabled) {
disable_irq_nosync(irq);
sc->irq_disabled = true;
}
mutex_unlock(&sc->irq_complete);
return IRQ_HANDLED;
}
sc->irq_waiting = false;
#if 1
/* TODO */
sc854x_check_fault_status(sc);
#endif
#if 0
sc854x_dump_reg(sc);
#endif
mutex_unlock(&sc->irq_complete);
#endif
power_supply_changed(sc->fc2_psy);
return IRQ_HANDLED;
}
static int sc854x_set_work_mode(struct sc854x *sc, int mode)
{
switch (mode)
{
case SC8545_STANDALONG:
case SC8546_STANDALONG:
case SC8547_STANDALONG:
case SC8549_STANDALONG:
sc->mode = STANDALONE;
break;
case SC8545_MASTER:
case SC8546_MASTER:
case SC8547_MASTER:
case SC8549_MASTER:
sc->mode = MASTER;
break;
case SC8545_SLAVE:
case SC8546_SLAVE:
case SC8547_SLAVE:
case SC8549_SLAVE:
sc->mode = SLAVE;
break;
default:
sc_err("Not find work mode\n");
return -ENODEV;
}
sc_err("[%s] work mode is %s\n", mode <= SC8545_SLAVE ?
"SC8545" : (mode <= SC8546_SLAVE ? "SC8546" : (mode <=
SC8547_SLAVE ? "SC8547" : "SC8549")), sc->mode == STANDALONE
? "standalone" : (sc->mode == MASTER ? "master" : "slave"));
return 0;
}
static int sc854x_detect_device(struct sc854x *sc)
{
int ret;
u8 data;
ret = sc854x_read_byte(sc, SC854X_REG_36, &data);
if (ret == 0) {
if(data != id_data[SC8545_ID] && data != id_data[SC8546_ID]
&& data != id_data[SC8547_ID] && data != id_data[SC8549_ID]) {
return -ENOMEM;
}
}
return ret;
}
static int sc854x_parse_dt(struct sc854x *sc, struct device *dev)
{
int ret;
struct device_node *np = dev->of_node;
sc->cfg = devm_kzalloc(dev, sizeof(struct sc854x_cfg),
GFP_KERNEL);
if (!sc->cfg)
return -ENOMEM;
if (of_property_read_string(np, "chg_name", &sc->cfg->chg_name) < 0) {
sc_err("%s no chg name, use default\n", __func__);
sc->cfg->chg_name = "primary_dvchg";//divider_charger
}
sc->cfg->bat_ovp_disable = of_property_read_bool(np,
"sc,sc854x,bat-ovp-disable");
sc->cfg->bat_ocp_disable = of_property_read_bool(np,
"sc,sc854x,bat-ocp-disable");
sc->cfg->vdrop_ovp_disable = of_property_read_bool(np,
"sc,sc854x,vdrop-ovp-disable");
sc->cfg->bus_ovp_disable = of_property_read_bool(np,
"sc,sc854x,bus-ovp-disable");
sc->cfg->bus_ucp_disable = of_property_read_bool(np,
"sc,sc854x,bus-ucp-disable");
sc->cfg->bus_ocp_disable = of_property_read_bool(np,
"sc,sc854x,bus-ocp-disable");
ret = of_property_read_u32(np, "sc,sc854x,bat-ovp-threshold",
&sc->cfg->bat_ovp_th);
if (ret) {
sc_err("failed to read bat-ovp-threshold\n");
return ret;
}
ret = of_property_read_u32(np, "sc,sc854x,bat-ocp-threshold",
&sc->cfg->bat_ocp_th);
if (ret) {
sc_err("failed to read bat-ocp-threshold\n");
return ret;
}
ret = of_property_read_u32(np, "sc,sc854x,ac-ovp-threshold",
&sc->cfg->ac_ovp_th);
if (ret) {
sc_err("failed to read ac-ovp-threshold\n");
return ret;
}
ret = of_property_read_u32(np, "sc,sc854x,bus-ovp-threshold",
&sc->cfg->bus_ovp_th);
if (ret) {
sc_err("failed to read bus-ovp-threshold\n");
return ret;
}
ret = of_property_read_u32(np, "sc,sc854x,bus-ocp-threshold",
&sc->cfg->bus_ocp_th);
if (ret) {
sc_err("failed to read bus-ocp-threshold\n");
return ret;
}
ret = of_property_read_u32(np, "sc,sc854x,sense-resistor-mohm",
&sc->cfg->sense_r_mohm);
if (ret) {
sc_err("failed to read sense-resistor-mohm\n");
return ret;
}
return 0;
}
static int sc854x_init_protection(struct sc854x *sc)
{
int ret;
ret = sc854x_enable_batovp(sc, !sc->cfg->bat_ovp_disable);
sc_info("%s bat ovp %s\n",
sc->cfg->bat_ovp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
ret = sc854x_enable_batocp(sc, !sc->cfg->bat_ocp_disable);
sc_info("%s bat ocp %s\n",
sc->cfg->bat_ocp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
ret = sc854x_enable_dropovp(sc, !sc->cfg->vdrop_ovp_disable);
sc_info("%s drop ovp %s\n",
sc->cfg->vdrop_ovp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
ret = sc854x_enable_busovp(sc, !sc->cfg->bus_ovp_disable);
sc_info("%s bus ovp %s\n",
sc->cfg->bus_ovp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
ret = sc854x_enable_busucp(sc, !sc->cfg->bus_ucp_disable);
sc_info("%s bus ucp %s\n",
sc->cfg->bus_ucp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
ret = sc854x_enable_busocp(sc, !sc->cfg->bus_ocp_disable);
sc_info("%s bus ocp %s\n",
sc->cfg->bus_ocp_disable ? "disable" : "enable",
!ret ? "successfullly" : "failed");
sc854x_voutovp_enabled(sc, true);
ret = sc854x_set_batovp_th(sc, sc->cfg->bat_ovp_th);
sc_info("set bat ovp th %d %s\n", sc->cfg->bat_ovp_th,
!ret ? "successfully" : "failed");
ret = sc854x_set_batocp_th(sc, sc->cfg->bat_ocp_th);
sc_info("set bat ocp threshold %d %s\n", sc->cfg->bat_ocp_th,
!ret ? "successfully" : "failed");
ret = sc854x_set_acovp_th(sc, sc->cfg->ac_ovp_th);
sc_info("set ac ovp threshold %d %s\n", sc->cfg->ac_ovp_th,
!ret ? "successfully" : "failed");
ret = sc854x_set_busovp_th(sc, sc->cfg->bus_ovp_th);
sc_info("set bus ovp threshold %d %s\n", sc->cfg->bus_ovp_th,
!ret ? "successfully" : "failed");
ret = sc854x_set_busocp_th(sc, sc->cfg->bus_ocp_th);
sc_info("set bus ocp threshold %d %s\n", sc->cfg->bus_ocp_th,
!ret ? "successfully" : "failed");
ret = sc854x_set_sense_resistor(sc, sc->cfg->sense_r_mohm);
sc_info("set sense r mohm %d %s\n", sc->cfg->sense_r_mohm,
!ret ? "successfully" : "failed");
return 0;
}
static int sc854x_init_adc(struct sc854x *sc)
{
sc854x_set_adc_scanrate(sc, false);
sc854x_set_adc_scan(sc, ADC_IBUS, true);
sc854x_set_adc_scan(sc, ADC_VBUS, true);
sc854x_set_adc_scan(sc, ADC_VOUT, true);
sc854x_set_adc_scan(sc, ADC_VBAT, true);
sc854x_set_adc_scan(sc, ADC_IBAT, true);
sc854x_set_adc_scan(sc, ADC_TDIE, true);
sc854x_set_adc_scan(sc, ADC_VAC, true);
sc854x_enable_adc(sc, true);
return 0;
}
static int sc854x_init_regulation(struct sc854x *sc)
{
sc854x_set_ibat_regulation(sc, 300);
sc854x_set_vbat_regulation(sc, 50);
sc854x_set_ibus_regulation(sc, 2400);
sc854x_reg_timeout_enabled(sc, true);
sc854x_set_vdrop_deglitch(sc, 5000);
sc854x_set_vdrop_ovp_th(sc, 400);
sc854x_ibat_regulation_enabled(sc, false);
sc854x_vbat_regulation_enabled(sc, false);
sc854x_ibus_regulation_enabled(sc, false);
return 0;
}
static int sc854x_init_device(struct sc854x *sc)
{
sc854x_reg_reset(sc, true);
sc854x_set_ss_timeout(sc, 5120);
sc854x_set_sense_resistor(sc, sc->cfg->sense_r_mohm);
sc854x_init_protection(sc);
sc854x_init_adc(sc);
sc854x_set_wdt(sc, 0);//disable WTD
sc854x_vbus_pd_enabled(sc, false);
sc854x_set_pmid2out_uvp(sc, -50);
sc854x_set_pmid2out_ovp(sc, 500);
sc854x_init_regulation(sc);
if (sc->mode == SLAVE) {
sc854x_disable_vbus_range(sc);
}
return 0;
}
/***********************MTK CHARGER OPS API************************/
static int sc854x_enable_chg(struct charger_device *chg_dev, bool en)
{
struct sc854x *sc = charger_get_data(chg_dev);
sc_info("%s: %s\n", __func__, en ? "enable" : "disable");
return sc854x_enable_charge(sc, !!en);
}
static int sc854x_is_chg_enabled(struct charger_device *chg_dev, bool *en)
{
struct sc854x *sc = charger_get_data(chg_dev);
return sc854x_check_charge_enabled(sc, en);
}
static inline enum SC854X_ADC_CH to_sc854x_adc(enum adc_channel chan)
{
switch (chan) {
case ADC_CHANNEL_VBUS:
return ADC_VBUS;
case ADC_CHANNEL_VBAT:
return ADC_VBAT;
case ADC_CHANNEL_IBUS:
return ADC_IBUS;
case ADC_CHANNEL_IBAT:
return ADC_IBAT;
case ADC_CHANNEL_TEMP_JC:
return ADC_TDIE;
case ADC_CHANNEL_VOUT:
return ADC_VOUT;
default:
break;
}
return ADC_MAX_NUM;
}
static int sc854x_get_adc(struct charger_device *chg_dev, enum adc_channel chan,
int *min, int *max)
{
int ret;
struct sc854x *sc = charger_get_data(chg_dev);
enum SC854X_ADC_CH _chan = to_sc854x_adc(chan);
if (_chan == ADC_MAX_NUM)
return -EINVAL;
ret = sc854x_get_adc_data(sc, _chan, max);
if (ret < 0)
return ret;
if (min != max)
*min = *max;
return ret;
}
static int sc854x_get_adc_accuracy(struct charger_device *chg_dev,
enum adc_channel chan, int *min, int *max)
{
enum SC854X_ADC_CH _chan = to_sc854x_adc(chan);
if (_chan == ADC_MAX_NUM)
return -EINVAL;
*min = *max = sc854x_adc_accuracy_tbl[_chan];
return 0;
}
static int sc854x_set_vbusovp(struct charger_device *chg_dev, u32 uV)
{
struct sc854x *sc = charger_get_data(chg_dev);
sc_info("%s: %d uV\n", __func__, uV)
return sc854x_set_busovp_th(sc, uV / 1000);
}
static int sc854x_set_ibusocp(struct charger_device *chg_dev, u32 uA)
{
struct sc854x *sc = charger_get_data(chg_dev);
sc_info("%s: %d uA\n", __func__, uA)
return sc854x_set_busocp_th(sc, uA / 1000);
}
static int sc854x_set_vbatovp(struct charger_device *chg_dev, u32 uV)
{
struct sc854x *sc = charger_get_data(chg_dev);
sc_info("%s: %d uV\n", __func__, uV)
return sc854x_set_batovp_th(sc, uV / 1000);
}
static int sc854x_set_ibatocp(struct charger_device *chg_dev, u32 uA)
{
struct sc854x *sc = charger_get_data(chg_dev);
sc_info("%s: %d uA\n", __func__, uA)
return sc854x_set_batocp_th(sc, uA / 1000);
}
static int sc854x_init_chip(struct charger_device *chg_dev)
{
struct sc854x *sc = charger_get_data(chg_dev);
return sc854x_init_device(sc);
}
static int sc854x_is_vbuslowerr(struct charger_device *chg_dev, bool *err)
{
int ret, reg_val;
struct sc854x *sc = charger_get_data(chg_dev);
ret = sc854x_check_vbus_error_status(sc, &reg_val);
if (ret < 0)
return ret;
sc_info("%s: reg val = 0x%02x\n", __func__, reg_val);
*err = !!(reg_val & SC854X_VBUS_ERRORLO_STAT_MASK);
return ret;
}
static int sc854x_set_vbatovp_alarm(struct charger_device *chg_dev, u32 uV)
{
return 0;
}
static int sc854x_reset_vbatovp_alarm(struct charger_device *chg_dev)
{
return 0;
}
static int sc854x_set_vbusovp_alarm(struct charger_device *chg_dev, u32 uV)
{
return 0;
}
static int sc854x_reset_vbusovp_alarm(struct charger_device *chg_dev)
{
return 0;
}
static const struct charger_ops sc854x_chg_ops = {
.enable = sc854x_enable_chg,
.is_enabled = sc854x_is_chg_enabled,
.get_adc = sc854x_get_adc,
.set_vbusovp = sc854x_set_vbusovp,
.set_ibusocp = sc854x_set_ibusocp,
.set_vbatovp = sc854x_set_vbatovp,
.set_ibatocp = sc854x_set_ibatocp,
.init_chip = sc854x_init_chip,
.set_vbatovp_alarm = sc854x_set_vbatovp_alarm,
.reset_vbatovp_alarm = sc854x_reset_vbatovp_alarm,
.set_vbusovp_alarm = sc854x_set_vbusovp_alarm,
.reset_vbusovp_alarm = sc854x_reset_vbusovp_alarm,
.is_vbuslowerr = sc854x_is_vbuslowerr,
.get_adc_accuracy = sc854x_get_adc_accuracy,
};
static int sc854x_register_chgdev(struct sc854x *sc)
{
sc_info("%s: chg name : %s\n", __func__, sc->cfg->chg_name);
sc->chg_prop.alias_name = sc->cfg->chg_name;
sc->chg_dev = charger_device_register(sc->cfg->chg_name, sc->dev,
sc, &sc854x_chg_ops,
&sc->chg_prop);
if (!sc->chg_dev)
return -EINVAL;
return 0;
}
static int sc854x_set_present(struct sc854x *sc, bool present)
{
sc->usb_present = present;
if (present)
sc854x_init_device(sc);
return 0;
}
static ssize_t sc854x_show_registers(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sc854x *sc = dev_get_drvdata(dev);
u8 addr;
u8 val;
u8 tmpbuf[300];
int len;
int idx = 0;
int ret;
idx = snprintf(buf, PAGE_SIZE, "%s:\n", "sc854x");
for (addr = 0x0; addr <= 0x3C; addr++) {
if((addr < 0x24) || (addr > 0x2B && addr < 0x33)
|| addr == 0x36 || addr == 0x3C) {
ret = sc854x_read_byte(sc, 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 sc854x_store_register(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sc854x *sc = dev_get_drvdata(dev);
int ret;
unsigned int reg;
unsigned int val;
ret = sscanf(buf, "%x %x", &reg, &val);
if (ret == 2 && reg <= 0x3C)
sc854x_write_byte(sc, (unsigned char)reg, (unsigned char)val);
return count;
}
static DEVICE_ATTR(registers, 0660, sc854x_show_registers, sc854x_store_register);
static void sc854x_create_device_node(struct device *dev)
{
device_create_file(dev, &dev_attr_registers);
}
static enum power_supply_property sc854x_charger_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_TYPE,
};
static int sc854x_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sc854x *sc = power_supply_get_drvdata(psy);
int result;
int ret;
sc_dbg(">>>>>psp = %d\n", psp);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
sc854x_check_charge_enabled(sc, &sc->charge_enabled);
val->intval = sc->charge_enabled;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = sc->usb_present;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = sc854x_get_adc_data(sc, ADC_VBUS, &result);
if (!ret)
sc->vbus_volt = result;
val->intval = sc->vbus_volt;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = sc854x_get_adc_data(sc, ADC_IBUS, &result);
if (!ret)
sc->ibus_curr = result;
val->intval = sc->ibus_curr;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = sc854x_get_adc_data(sc, ADC_VBAT, &result);
if (!ret)
sc->vbat_volt = result;
val->intval = sc->vbat_volt;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = sc854x_get_adc_data(sc, ADC_IBAT, &result);
if (!ret)
sc->ibat_curr = result;
val->intval = sc->ibat_curr;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = sc854x_get_adc_data(sc, ADC_TDIE, &result);
if (!ret)
sc->die_temp = result;
val->intval = sc->die_temp;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
ret = sc854x_check_vbus_error_status(sc, &result);
if (!ret)
sc->vbus_error = result;
val->intval = sc->vbus_error;
break;
default:
return -EINVAL;
}
return 0;
}
static int sc854x_charger_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct sc854x *sc = power_supply_get_drvdata(psy);
sc_err("prop = %d\n", prop);
switch (prop) {
case POWER_SUPPLY_PROP_ONLINE:
sc854x_enable_charge(sc, val->intval);
sc854x_check_charge_enabled(sc, &sc->charge_enabled);
sc_info("POWER_SUPPLY_PROP_ONLINE: %s\n",
val->intval ? "enable" : "disable");
break;
case POWER_SUPPLY_PROP_PRESENT:
sc854x_set_present(sc, val->intval);
break;
default:
return -EINVAL;
}
return 0;
}
static int sc854x_charger_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
int ret;
switch (prop) {
case POWER_SUPPLY_PROP_ONLINE:
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
static int sc854x_psy_register(struct sc854x *sc)
{
sc->psy_cfg.drv_data = sc;
sc->psy_cfg.of_node = sc->dev->of_node;
if (sc->mode == STANDALONE) {
sc->psy_desc.name = "sc854x-standalone";
} else if (sc->mode == MASTER) {
sc->psy_desc.name = "sc854x-master";
} else {
sc->psy_desc.name = "sc854x-slave";
}
sc->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
sc->psy_desc.properties = sc854x_charger_props;
sc->psy_desc.num_properties = ARRAY_SIZE(sc854x_charger_props);
sc->psy_desc.get_property = sc854x_charger_get_property;
sc->psy_desc.set_property = sc854x_charger_set_property;
sc->psy_desc.property_is_writeable = sc854x_charger_is_writeable;
sc->fc2_psy = devm_power_supply_register(sc->dev,
&sc->psy_desc, &sc->psy_cfg);
if (IS_ERR(sc->fc2_psy)) {
sc_err("failed to register fc2_psy\n");
return PTR_ERR(sc->fc2_psy);
}
sc->cw_bat = power_supply_get_by_name("cw-bat");
if(!sc->cw_bat){
sc_err("gezi failed to get cw_bat\n");
}
sc->charge_psy = power_supply_get_by_name("charger");
if(!sc->charge_psy){
sc_err("gezi failed to get charge_psy\n");
}
sc_info("%s power supply register successfully\n", sc->psy_desc.name);
return 0;
}
static int sc854x_irq_register(struct sc854x *sc)
{
int ret;
struct device_node *node = sc->dev->of_node;
if (!node) {
sc_err("device tree node missing\n");
return -EINVAL;
}
sc->irq_gpio = of_get_named_gpio(node, "sc,sc854x,irq-gpio", 0);
if (!gpio_is_valid(sc->irq_gpio)) {
sc_err("fail to valid gpio : %d\n", sc->irq_gpio);
return -EINVAL;
}
if (gpio_is_valid(sc->irq_gpio)) {
ret = gpio_request_one(sc->irq_gpio, GPIOF_DIR_IN,"sc854x_irq");
if (ret) {
sc_err("failed to request sc854x_irq\n");
return -EINVAL;
}
sc->irq = gpio_to_irq(sc->irq_gpio);
if (sc->irq < 0) {
sc_err("failed to gpio_to_irq\n");
return -EINVAL;
}
} else {
sc_err("irq gpio not provided\n");
return -EINVAL;
}
if (sc->irq) {
if (sc->mode == STANDALONE) {
ret = devm_request_threaded_irq(&sc->client->dev, sc->irq,
NULL, sc854x_charger_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"sc854x standalone irq", sc);
} else if (sc->mode == MASTER) {
ret = devm_request_threaded_irq(&sc->client->dev, sc->irq,
NULL, sc854x_charger_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"sc854x master irq", sc);
} else {
ret = devm_request_threaded_irq(&sc->client->dev, sc->irq,
NULL, sc854x_charger_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"sc854x slave irq", sc);
}
if (ret < 0) {
sc_err("request irq for irq=%d failed, ret =%d\n",
sc->irq, ret);
return ret;
}
enable_irq_wake(sc->irq);
}
return ret;
}
static void sc854x_check_fault_status(struct sc854x *sc)
{
int ret;
u8 flag = 0;
mutex_lock(&sc->data_lock);
ret = sc854x_read_byte(sc, SC854X_REG_02, &flag);
if (!ret && (flag & SC854X_AC_OVP_FLAG_MASK))
sc_err("irq VAC OVP FLAG\n");
ret = sc854x_read_byte(sc, SC854X_REG_03, &flag);
if (!ret && (flag & SC854X_VDROP_OVP_FLAG_MASK))
sc_err("irq CDROP OVP FLAG\n");
ret = sc854x_read_byte(sc, SC854X_REG_06, &flag);
if (!ret && flag) {
if (flag & SC854X_TSHUT_FLAG_MASK)
sc_err("irq TSHUT FLAG\n");
if (flag & SC854X_SS_TIMEOUT_FLAG_MASK)
sc_err("irq SS TIMEOUT FLAG\n");
if (flag & SC854X_REG_TIMEOUT_FLAG_MASK)
sc_err("irq REG TIMEOUT FLAG\n");
if (flag & SC854X_PIN_DIAG_FALL_FLAG_MASK)
sc_err("irq PIN DIAG FALL FLAG\n");
}
ret = sc854x_read_byte(sc, SC854X_REG_09, &flag);
if (!ret && flag) {
if (flag & SC854X_POR_FLAG_MASK) {
sc_err("irq POR FLAG\n");
sc854x_set_present(sc, true);
}
if (flag & SC854X_IBUS_UCP_RISE_FLAG_MASK)
sc_err("irq IBUS UCP RIST FLAG");
if (flag & SC854X_WD_TIMEOUT_FLAG_MASK)
sc_err("irq WD TIMEOUT FLAG\n");
}
ret = sc854x_read_byte(sc, SC854X_REG_0A, &flag);
if (!ret && (flag & SC854X_VBATREG_ACTIVE_FLAG_MASK))
sc_err("irq VBATREG ACTIVE FLAG\n");
ret = sc854x_read_byte(sc, SC854X_REG_0B, &flag);
if (!ret && (flag & SC854X_IBATREG_ACTIVE_FLAG_MASK))
sc_err("irq IBATREG ACTIVE FLAG\n");
ret = sc854x_read_byte(sc, SC854X_REG_0C, &flag);
if (!ret && (flag & SC854X_IBUSREG_ACTIVE_FLAG_MASK))
sc_err("irq IBUSREG ACTIVE FLAG\n");
ret = sc854x_read_byte(sc, SC854X_REG_0D, &flag);
if (!ret && flag) {
if (flag & SC854X_PMID2OUT_UVP_FLAG_MASK)
sc_err("irq PMID2OUT UVP FLAG\n");
if (flag & SC854X_PMID2OUT_OVP_FLAG_MASK)
sc_err("irq PMID2OUT OVP FLAG\n");
}
ret = sc854x_read_byte(sc, SC854X_REG_0F, &flag);
if (!ret && flag) {
if (flag & SC854X_VOUT_OVP_FLAG_MASK)
sc_err("irq VOUT OVP FLAG\n");
if (flag & SC854X_VBAT_OVP_FLAG_MASK)
sc_err("irq VBAT OVP FLAG\n");
if (flag & SC854X_IBAT_OCP_FLAG_MASK)
sc_err("irq IBAT OCP FLAG\n");
if (flag & SC854X_VBUS_OVP_FLAG_MASK)
sc_err("irq VBUS OVP FLAG\n");
if (flag & SC854X_IBUS_OCP_FLAG_MASK)
sc_err("irq IBUS OCP FLAG\n");
if (flag & SC854X_IBUS_UCP_FALL_FLAG_MASK)
sc_err("irq IBUS UCP FALL FLAG\n");
if (flag & SC854X_ADAPTER_INSERT_FLAG_MASK)
sc_err("irq ADAPTER INSERT FLAG\n");
if (flag & SC854X_VBAT_INSERT_FLAG_MASK)
sc_err("irq VBAT INSERT FLAG\n");
}
mutex_unlock(&sc->data_lock);
}
static void determine_initial_status(struct sc854x *sc)
{
if (sc->client->irq)
sc854x_charger_interrupt(sc->client->irq, sc);
}
static struct of_device_id sc854x_charger_match_table[] = {
{ .compatible = "sc,sc8545-standalone",
.data = &sc854x_mode_data[SC8545_STANDALONG],},
{ .compatible = "sc,sc8545-master",
.data = &sc854x_mode_data[SC8545_MASTER],},
{ .compatible = "sc,sc8545-slave",
.data = &sc854x_mode_data[SC8545_SLAVE],},
{ .compatible = "sc,sc8546-standalone",
.data = &sc854x_mode_data[SC8546_STANDALONG],},
{ .compatible = "sc,sc8546-master",
.data = &sc854x_mode_data[SC8546_MASTER],},
{ .compatible = "sc,sc8546-slave",
.data = &sc854x_mode_data[SC8546_SLAVE],},
{ .compatible = "sc,sc8547-standalone",
.data = &sc854x_mode_data[SC8547_STANDALONG],},
{ .compatible = "sc,sc8547-master",
.data = &sc854x_mode_data[SC8547_MASTER],},
{ .compatible = "sc,sc8547-slave",
.data = &sc854x_mode_data[SC8547_SLAVE],},
{ .compatible = "sc,sc8549-standalone",
.data = &sc854x_mode_data[SC8549_STANDALONG],},
{ .compatible = "sc,sc8549-master",
.data = &sc854x_mode_data[SC8549_MASTER],},
{ .compatible = "sc,sc8549-slave",
.data = &sc854x_mode_data[SC8549_SLAVE],},
{},
};
static int sc854x_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sc854x *sc;
const struct of_device_id *match;
struct device_node *node = client->dev.of_node;
int ret;
sc = devm_kzalloc(&client->dev, sizeof(struct sc854x), GFP_KERNEL);
if (!sc)
return -ENOMEM;
sc->dev = &client->dev;
sc->client = client;
mutex_init(&sc->i2c_rw_lock);
mutex_init(&sc->data_lock);
mutex_init(&sc->irq_complete);
sc->resume_completed = true;
sc->irq_waiting = false;
sc->sc_adc_disabled = false;
ret = sc854x_detect_device(sc);
if (ret) {
sc_err("No sc854x device found!\n");
//prize add by lipengpeng 20220913 start
#if IS_ENABLED(CONFIG_FACTORY_CHARGE)&&IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT)
is_chg2_exist=0;
#endif
//prize add by lipengpeng 20220913 end
goto err_1;
}
//prize add by lipengpeng 20220913 start
else{
sc_err("sc854x device found!\n");
#if IS_ENABLED(CONFIG_FACTORY_CHARGE)&&IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT)
is_chg2_exist=1;
#endif
}
//prize add by lipengpeng 20220913 end
i2c_set_clientdata(client, sc);
sc854x_create_device_node(&(client->dev));
match = of_match_node(sc854x_charger_match_table, node);
if (match == NULL) {
sc_err("device tree match not found!\n");
goto err_1;
}
sc854x_set_work_mode(sc, *(int *)match->data);
if (ret) {
goto err_1;
}
ret = sc854x_parse_dt(sc, &client->dev);
if (ret) {
goto err_1;
}
ret = sc854x_init_device(sc);
if (ret) {
sc_err("Failed to init device\n");
goto err_1;
}
ret = sc854x_psy_register(sc);
if (ret) {
goto err_2;
}
ret = sc854x_irq_register(sc);
if (ret < 0) {
goto err_2;
}
ret = sc854x_register_chgdev(sc);
if (ret < 0) {
sc_err("%s reg chgdev fail(%d)\n", __func__, ret);
goto err_2;
}
device_init_wakeup(sc->dev, 1);
determine_initial_status(sc);
sc_info("sc854x probe successfully!\n");
return 0;
err_2:
power_supply_unregister(sc->fc2_psy);
err_1:
mutex_destroy(&sc->i2c_rw_lock);
mutex_destroy(&sc->data_lock);
mutex_destroy(&sc->irq_complete);
sc_info("sc854x probe fail\n");
devm_kfree(&client->dev, sc);
return ret;
}
static inline bool is_device_suspended(struct sc854x *sc)
{
return !sc->resume_completed;
}
static int sc854x_suspend_disable_adc(struct sc854x *sc)
{
union power_supply_propval prop;
int ret = 0;
if(!sc){
pr_err("gezi------%s---sc null----%d\n",__func__,__LINE__);
return -1;
}
if(!sc->charge_psy){
sc->charge_psy = power_supply_get_by_name("charger");
if(!sc->charge_psy){
pr_err("gezi failed to get charge_psy\n");
return -1;
}
else{
ret = power_supply_get_property(sc->charge_psy,POWER_SUPPLY_PROP_ONLINE, &prop);
pr_err("gezi------%s----online:%d dis:%d\n",__func__,prop.intval,sc->sc_adc_disabled);
if((!prop.intval) && (!sc->sc_adc_disabled)){
sc->sc_adc_disabled = true;
sc854x_enable_adc(sc, false);
}
}
}
else{
ret = power_supply_get_property(sc->charge_psy,POWER_SUPPLY_PROP_ONLINE, &prop);
pr_err("gezi------%s----online:%d dis:%d\n",__func__,prop.intval,sc->sc_adc_disabled);
if((!prop.intval) && (!sc->sc_adc_disabled)){
sc->sc_adc_disabled = true;
sc854x_enable_adc(sc, false);
}
}
return 0;
}
static int sc854x_resume_enable_adc(struct sc854x *sc)
{
//union power_supply_propval prop;
//int ret = 0;
if(!sc){
pr_err("gezi------%s---sc null----%d\n",__func__,__LINE__);
return -1;
}
pr_err("gezi------%s----dis:%d\n",__func__,sc->sc_adc_disabled);
if(sc->sc_adc_disabled){
sc854x_enable_adc(sc, true);
sc->sc_adc_disabled = false;
}
return 0;
}
static int sc854x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sc854x *sc = i2c_get_clientdata(client);
pr_err("gezi------%s-------%d\n",__func__,__LINE__);
mutex_lock(&sc->irq_complete);
sc854x_suspend_disable_adc(sc);
sc->resume_completed = false;
mutex_unlock(&sc->irq_complete);
sc_err("Suspend successfully!");
return 0;
}
static int sc854x_suspend_noirq(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sc854x *sc = i2c_get_clientdata(client);
if (sc->irq_waiting) {
pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
return -EBUSY;
}
return 0;
}
static int sc854x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sc854x *sc = i2c_get_clientdata(client);
pr_err("gezi------%s-------%d\n",__func__,__LINE__);
mutex_lock(&sc->irq_complete);
sc854x_resume_enable_adc(sc);
sc->resume_completed = true;
if (sc->irq_waiting) {
sc->irq_disabled = false;
enable_irq(client->irq);
mutex_unlock(&sc->irq_complete);
sc854x_charger_interrupt(client->irq, sc);
} else {
mutex_unlock(&sc->irq_complete);
}
power_supply_changed(sc->fc2_psy);
sc_err("Resume successfully!");
return 0;
}
static int sc854x_charger_remove(struct i2c_client *client)
{
struct sc854x *sc = i2c_get_clientdata(client);
pr_err("gezi------%s-------%d\n",__func__,__LINE__);
sc854x_enable_adc(sc, false);
charger_device_unregister(sc->chg_dev);
power_supply_unregister(sc->fc2_psy);
mutex_destroy(&sc->data_lock);
mutex_destroy(&sc->i2c_rw_lock);
mutex_destroy(&sc->irq_complete);
return 0;
}
static void sc854x_charger_shutdown(struct i2c_client *client)
{
//pr_err("gezi------%s-------%d\n",__func__,__LINE__);
struct sc854x *sc = i2c_get_clientdata(client);
if(!sc){
pr_err("gezi------%s---sc null----%d\n",__func__,__LINE__);
return;
}
sc854x_enable_adc(sc, false);
}
static const struct dev_pm_ops sc854x_pm_ops = {
.resume = sc854x_resume,
.suspend_noirq = sc854x_suspend_noirq,
.suspend = sc854x_suspend,
};
static struct i2c_driver sc854x_charger_driver = {
.driver = {
.name = "sc854x-charger",
.owner = THIS_MODULE,
.of_match_table = sc854x_charger_match_table,
.pm = &sc854x_pm_ops,
},
.probe = sc854x_charger_probe,
.remove = sc854x_charger_remove,
.shutdown = sc854x_charger_shutdown,
};
module_i2c_driver(sc854x_charger_driver);
MODULE_DESCRIPTION("SC SC854X Charge Pump Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("South Chip <Aiden-yu@southchip.com>");