blob: 8aed122ebdebcf9ccf785df6d82d6b6979bc752d [file] [log] [blame]
/* Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__
#include <linux/spmi.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/bitops.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/spmi.h>
#include <linux/printk.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
#include <linux/rtc.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/batterydata-lib.h>
#include <linux/of_batterydata.h>
#include <linux/msm_bcl.h>
#include <linux/ktime.h>
#include <linux/usb/typec.h>
/* Mask/Bit helpers */
#define _SMB_MASK(BITS, POS) \
((unsigned char)(((1 << (BITS)) - 1) << (POS)))
#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
_SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
(RIGHT_BIT_POS))
#define STAGE_WORK_DELAY_MS 30000
#define STAGE_COUNT_MAX 2
#define CHG_MONITOR_WORK_DELAY_MS 30000
#define HIGH_CURRENT_COUNT_MAX 2
#define DELTA_MV 100
#define DEFAULT_USB_MA 100
#define HVDCP_MA 1800
/* Parameters for ibus compass compensation */
#define POWER_CONSUMPTION 500
#define COMPENSATION_RATIO 96
#define CHARGE_STAGE_DELTA_MV 50
#define COMPENSATION_LEN 16
#define COMPENSATION_DELAY_MS 5000
#define TAPER_IRQ_LIMIT_SECONDS 5
/* Config registers */
struct smbchg_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
};
struct parallel_usb_cfg {
struct power_supply *psy;
int min_current_thr_ma;
int min_9v_current_thr_ma;
int allowed_lowering_ma;
int current_max_ma;
bool avail;
struct mutex lock;
int initial_aicl_ma;
ktime_t last_disabled;
bool enabled_once;
};
struct ilim_entry {
int vmin_uv;
int vmax_uv;
int icl_pt_ma;
int icl_lv_ma;
int icl_hv_ma;
};
struct ilim_map {
int num;
struct ilim_entry *entries;
};
struct smbchg_chip {
struct device *dev;
struct spmi_device *spmi;
/* peripheral register address bases */
u16 chgr_base;
u16 bat_if_base;
u16 usb_chgpth_base;
u16 dc_chgpth_base;
u16 otg_base;
u16 misc_base;
int fake_battery_soc;
u8 revision[4];
/* configuration parameters */
int iterm_ma;
int usb_max_current_ma;
int dc_max_current_ma;
int usb_target_current_ma;
int usb_tl_current_ma;
int dc_target_current_ma;
int target_fastchg_current_ma;
int cfg_fastchg_current_ma;
int fastchg_current_ma;
int vfloat_mv;
int fastchg_current_comp;
int float_voltage_comp;
int resume_delta_mv;
int safety_time;
int prechg_safety_time;
int bmd_pin_src;
int jeita_temp_hard_limit;
int cfg_vfloat_mv;
int current_stage_thr_mv;
int current_stage_delta_mv;
int current_stage_ma;
int current_stage_count;
int parallel_current_limited;
int parallel_voltage_checked;
unsigned long charge_stage_convert_time;
int warm_current_ma;
int cool_current_ma;
int soft_temp_comp;
int warm_bat_decidegc;
int high_current_thr_ma;
int high_current_count;
int max_input_current_ma;
int float_voltage_comp_stage;
bool use_vfloat_adjustments;
bool iterm_disabled;
bool bmd_algo_disabled;
bool soft_vfloat_comp_disabled;
bool chg_enabled;
bool low_icl_wa_on;
bool charge_unknown_battery;
bool chg_inhibit_en;
bool chg_inhibit_source_fg;
bool low_volt_dcin;
bool vbat_above_headroom;
bool force_aicl_rerun;
bool temp_comp_done;
u8 original_usbin_allowance;
struct parallel_usb_cfg parallel;
struct delayed_work parallel_en_work;
struct dentry *debug_root;
/* wipower params */
struct ilim_map wipower_default;
struct ilim_map wipower_pt;
struct ilim_map wipower_div2;
struct qpnp_vadc_chip *vadc_dev;
bool wipower_dyn_icl_avail;
struct ilim_entry current_ilim;
struct mutex wipower_config;
bool wipower_configured;
struct qpnp_adc_tm_btm_param param;
/* flash current prediction */
int rpara_uohm;
int rslow_uohm;
int vled_max_uv;
/* vfloat adjustment */
int max_vbat_sample;
int n_vbat_samples;
/* status variables */
int battchg_disabled;
int usb_suspended;
int dc_suspended;
int wake_reasons;
int previous_soc;
int usb_online;
bool dc_present;
bool usb_present;
bool batt_present;
int otg_retries;
ktime_t otg_enable_time;
bool aicl_deglitch_short;
bool sw_esr_pulse_en;
bool safety_timer_en;
bool aicl_complete;
bool usb_ov_det;
bool otg_pulse_skip_dis;
const char *battery_type;
bool very_weak_charger;
bool parallel_charger_detected;
/* jeita and temperature */
bool batt_hot;
bool batt_cold;
bool batt_warm;
bool batt_cool;
bool batt_ov;
unsigned int thermal_levels;
unsigned int therm_lvl_sel;
unsigned int *thermal_mitigation;
unsigned int *thermal_mitigation_hvdcp;
/* irqs */
int batt_hot_irq;
int batt_warm_irq;
int batt_cool_irq;
int batt_cold_irq;
int batt_missing_irq;
int vbat_low_irq;
int chg_hot_irq;
int chg_term_irq;
int taper_irq;
bool taper_irq_enabled;
struct mutex taper_irq_lock;
int recharge_irq;
int fastchg_irq;
int safety_timeout_irq;
int power_ok_irq;
int dcin_uv_irq;
int usbin_uv_irq;
int usbin_ov_irq;
int src_detect_irq;
int otg_fail_irq;
int otg_oc_irq;
int aicl_done_irq;
int usbid_change_irq;
int chg_error_irq;
bool enable_aicl_wake;
/* psy */
struct power_supply *usb_psy;
struct power_supply batt_psy;
struct power_supply dc_psy;
struct power_supply *bms_psy;
int dc_psy_type;
const char *bms_psy_name;
const char *battery_psy_name;
bool psy_registered;
struct smbchg_regulator otg_vreg;
struct smbchg_regulator ext_otg_vreg;
struct work_struct usb_set_online_work;
struct work_struct resume_to_normal_work;
struct delayed_work vfloat_adjust_work;
struct delayed_work hvdcp_det_work;
struct delayed_work current_stage_work;
struct delayed_work monitor_charging_work;
spinlock_t sec_access_lock;
struct mutex current_change_lock;
struct mutex usb_set_online_lock;
struct mutex battchg_disabled_lock;
struct mutex usb_en_lock;
struct mutex dc_en_lock;
struct mutex fcc_lock;
struct mutex pm_lock;
struct mutex otg_lock;
/* aicl deglitch workaround */
unsigned long first_aicl_seconds;
int aicl_irq_count;
struct mutex usb_status_lock;
char compass_compensation[COMPENSATION_LEN];
};
enum print_reason {
PR_REGISTER = BIT(0),
PR_INTERRUPT = BIT(1),
PR_STATUS = BIT(2),
PR_DUMP = BIT(3),
PR_PM = BIT(4),
PR_MISC = BIT(5),
PR_WIPOWER = BIT(6),
};
enum wake_reason {
PM_PARALLEL_CHECK = BIT(0),
PM_REASON_VFLOAT_ADJUST = BIT(1),
PM_ESR_PULSE = BIT(2),
PM_PARALLEL_TAPER = BIT(3),
PM_CHARGING_CHECK = BIT(7),
};
static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv);
static void update_compass_compensation(struct power_supply *psy);
static int get_current_time(unsigned long *now_tm_sec);
static int smbchg_is_hvdcp(struct smbchg_chip *chip);
static int smbchg_debug_mask;
module_param_named(
debug_mask, smbchg_debug_mask, int, S_IRUSR | S_IWUSR
);
static int smbchg_parallel_en = 1;
module_param_named(
parallel_en, smbchg_parallel_en, int, S_IRUSR | S_IWUSR
);
static int wipower_dyn_icl_en;
module_param_named(
dynamic_icl_wipower_en, wipower_dyn_icl_en,
int, S_IRUSR | S_IWUSR
);
static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS;
module_param_named(
wipower_dcin_interval, wipower_dcin_interval,
int, S_IRUSR | S_IWUSR
);
#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000
static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV;
module_param_named(
wipower_dcin_hyst_uv, wipower_dcin_hyst_uv,
int, S_IRUSR | S_IWUSR
);
static bool off_charge_flag;
#define pr_smb(reason, fmt, ...) \
do { \
if (smbchg_debug_mask & (reason)) \
pr_info(fmt, ##__VA_ARGS__); \
else \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
#define pr_smb_rt(reason, fmt, ...) \
do { \
if (smbchg_debug_mask & (reason)) \
pr_info_ratelimited(fmt, ##__VA_ARGS__); \
else \
pr_debug_ratelimited(fmt, ##__VA_ARGS__); \
} while (0)
static int smbchg_read(struct smbchg_chip *chip, u8 *val,
u16 addr, int count)
{
int rc = 0;
struct spmi_device *spmi = chip->spmi;
if (addr == 0) {
dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return -EINVAL;
}
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, count);
if (rc) {
dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return rc;
}
return 0;
}
/*
* Writes an arbitrary number of bytes to a specified register
*
* Do not use this function for register writes if possible. Instead use the
* smbchg_masked_write function.
*
* The sec_access_lock must be held for all register writes and this function
* does not do that. If this function is used, please hold the spinlock or
* random secure access writes may fail.
*/
static int smbchg_write(struct smbchg_chip *chip, u8 *val,
u16 addr, int count)
{
int rc = 0;
struct spmi_device *spmi = chip->spmi;
if (addr == 0) {
dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return -EINVAL;
}
rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, count);
if (rc) {
dev_err(chip->dev, "write failed addr=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return rc;
}
return 0;
}
/*
* Writes a register to the specified by the base and limited by the bit mask
*
* Do not use this function for register writes if possible. Instead use the
* smbchg_masked_write function.
*
* The sec_access_lock must be held for all register writes and this function
* does not do that. If this function is used, please hold the spinlock or
* random secure access writes may fail.
*/
static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
u8 val)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg, base, 1);
if (rc) {
dev_err(chip->dev, "spmi read failed: addr=%03X, rc=%d\n",
base, rc);
return rc;
}
reg &= ~mask;
reg |= val & mask;
pr_smb(PR_REGISTER, "addr = 0x%x writing 0x%x\n", base, reg);
rc = smbchg_write(chip, &reg, base, 1);
if (rc) {
dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
base, rc);
return rc;
}
return 0;
}
/*
* Writes a register to the specified by the base and limited by the bit mask
*
* This function holds a spin lock to ensure secure access register writes goes
* through. If the secure access unlock register is armed, any old register
* write can unarm the secure access unlock, causing the next write to fail.
*
* Note: do not use this for sec_access registers. Instead use the function
* below: smbchg_sec_masked_write
*/
static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
u8 val)
{
unsigned long flags;
int rc;
spin_lock_irqsave(&chip->sec_access_lock, flags);
rc = smbchg_masked_write_raw(chip, base, mask, val);
spin_unlock_irqrestore(&chip->sec_access_lock, flags);
return rc;
}
/*
* Unlocks sec access and writes to the register specified.
*
* This function holds a spin lock to exclude other register writes while
* the two writes are taking place.
*/
#define SEC_ACCESS_OFFSET 0xD0
#define SEC_ACCESS_VALUE 0xA5
#define PERIPHERAL_MASK 0xFF
static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
u8 val)
{
unsigned long flags;
int rc;
u16 peripheral_base = base & (~PERIPHERAL_MASK);
spin_lock_irqsave(&chip->sec_access_lock, flags);
rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET,
SEC_ACCESS_VALUE, SEC_ACCESS_VALUE);
if (rc) {
dev_err(chip->dev, "Unable to unlock sec_access: %d", rc);
goto out;
}
rc = smbchg_masked_write_raw(chip, base, mask, val);
out:
spin_unlock_irqrestore(&chip->sec_access_lock, flags);
return rc;
}
static void smbchg_stay_awake(struct smbchg_chip *chip, int reason)
{
int reasons;
mutex_lock(&chip->pm_lock);
reasons = chip->wake_reasons | reason;
if (reasons != 0 && chip->wake_reasons == 0) {
pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n",
reasons, reason);
pm_stay_awake(chip->dev);
}
chip->wake_reasons = reasons;
mutex_unlock(&chip->pm_lock);
}
static void smbchg_relax(struct smbchg_chip *chip, int reason)
{
int reasons;
mutex_lock(&chip->pm_lock);
reasons = chip->wake_reasons & (~reason);
if (reasons == 0 && chip->wake_reasons != 0) {
pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n",
reasons, reason);
pm_relax(chip->dev);
}
chip->wake_reasons = reasons;
mutex_unlock(&chip->pm_lock);
};
enum pwr_path_type {
UNKNOWN = 0,
PWR_PATH_BATTERY = 1,
PWR_PATH_USB = 2,
PWR_PATH_DC = 3,
};
#define PWR_PATH 0x08
#define PWR_PATH_MASK 0x03
static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + PWR_PATH, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc);
return PWR_PATH_BATTERY;
}
return reg & PWR_PATH_MASK;
}
#define RID_STS 0xB
#define RID_MASK 0xF
#define IDEV_STS 0x8
#define RT_STS 0x10
#define USBID_MSB 0xE
#define USBIN_UV_BIT BIT(0)
#define USBIN_OV_BIT BIT(1)
#define USBIN_SRC_DET_BIT BIT(2)
#define FMB_STS_MASK SMB_MASK(3, 0)
#define USBID_GND_THRESHOLD 0x495
#ifndef CONFIG_TYPEC
static bool is_otg_present(struct smbchg_chip *chip)
{
int rc;
u8 reg;
u8 usbid_reg[2];
u16 usbid_val;
/*
* There is a problem with USBID conversions on PMI8994 revisions
* 2.0.0. As a workaround, check that the cable is not
* detected as factory test before enabling OTG.
*/
rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc);
return false;
}
if ((reg & FMB_STS_MASK) != 0) {
pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg);
return false;
}
rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc);
return false;
}
usbid_val = (usbid_reg[0] << 8) | usbid_reg[1];
if (usbid_val > USBID_GND_THRESHOLD) {
pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n",
usbid_val);
return false;
}
rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RID_STS, 1);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't read usb rid status rc = %d\n", rc);
return false;
}
pr_smb(PR_STATUS, "RID_STS = %02x\n", reg);
return (reg & RID_MASK) == 0;
}
#endif
#define USBIN_9V BIT(5)
#define USBIN_UNREG BIT(4)
#define USBIN_LV BIT(3)
#define DCIN_9V BIT(2)
#define DCIN_UNREG BIT(1)
#define DCIN_LV BIT(0)
#define INPUT_STS 0x0D
#define DCIN_UV_BIT BIT(0)
#define DCIN_OV_BIT BIT(1)
static bool is_dc_present(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg, chip->dc_chgpth_base + RT_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc);
return false;
}
if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT))
return false;
return true;
}
static bool is_usb_present(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
return false;
}
if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT))
return false;
rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + INPUT_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
return false;
}
return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
}
static char *usb_type_str[] = {
"SDP", /* bit 0 */
"OTHER", /* bit 1 */
"DCP", /* bit 2 */
"CDP", /* bit 3 */
"NONE", /* bit 4 error case */
};
#define N_TYPE_BITS 4
#define TYPE_BITS_OFFSET 4
static int get_type(u8 type_reg)
{
unsigned long type = type_reg;
type >>= TYPE_BITS_OFFSET;
return find_first_bit(&type, N_TYPE_BITS);
}
/* helper to return the string of USB type */
static inline char *get_usb_type_name(int type)
{
return usb_type_str[type];
}
static enum power_supply_type usb_type_enum[] = {
POWER_SUPPLY_TYPE_USB, /* bit 0 */
POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */
POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */
POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */
POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */
};
/* helper to return enum power_supply_type of USB type */
static inline enum power_supply_type get_usb_supply_type(int type)
{
return usb_type_enum[type];
}
static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name,
enum power_supply_type *usb_supply_type)
{
int rc, type;
u8 reg;
rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
*usb_type_name = "Other";
*usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
}
type = get_type(reg);
*usb_type_name = get_usb_type_name(type);
*usb_supply_type = get_usb_supply_type(type);
}
static enum power_supply_property smbchg_battery_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_FLASH_ACTIVE,
POWER_SUPPLY_PROP_PROFILE_STATUS,
POWER_SUPPLY_PROP_COMPASS_COMPENSATION,
};
#define CHGR_STS 0x0E
#define BATT_LESS_THAN_2V BIT(4)
#define CHG_HOLD_OFF_BIT BIT(3)
#define CHG_TYPE_MASK SMB_MASK(2, 1)
#define CHG_TYPE_SHIFT 1
#define BATT_NOT_CHG_VAL 0x0
#define BATT_PRE_CHG_VAL 0x1
#define BATT_FAST_CHG_VAL 0x2
#define BATT_TAPER_CHG_VAL 0x3
#define CHG_EN_BIT BIT(0)
#define CHG_INHIBIT_BIT BIT(1)
#define BAT_TCC_REACHED_BIT BIT(7)
static int get_prop_batt_status(struct smbchg_chip *chip)
{
int rc, status = POWER_SUPPLY_STATUS_DISCHARGING;
u8 reg = 0, chg_type;
bool charger_present, chg_inhibit;
charger_present = is_usb_present(chip) | is_dc_present(chip);
if (!charger_present)
return POWER_SUPPLY_STATUS_DISCHARGING;
rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
return POWER_SUPPLY_STATUS_UNKNOWN;
}
if (reg & BAT_TCC_REACHED_BIT)
return POWER_SUPPLY_STATUS_FULL;
chg_inhibit = reg & CHG_INHIBIT_BIT;
if (chg_inhibit)
return POWER_SUPPLY_STATUS_FULL;
rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
return POWER_SUPPLY_STATUS_UNKNOWN;
}
if (reg & CHG_HOLD_OFF_BIT) {
/*
* when chg hold off happens the battery is
* not charging
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
goto out;
}
chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
if (chg_type == BATT_NOT_CHG_VAL)
status = POWER_SUPPLY_STATUS_DISCHARGING;
else
status = POWER_SUPPLY_STATUS_CHARGING;
out:
pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg);
return status;
}
#define BAT_PRES_STATUS 0x08
#define BAT_PRES_BIT BIT(7)
static int get_prop_batt_present(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg, chip->bat_if_base + BAT_PRES_STATUS, 1);
if (rc < 0) {
dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
return 0;
}
return !!(reg & BAT_PRES_BIT);
}
static int get_prop_charge_type(struct smbchg_chip *chip)
{
int rc;
u8 reg, chg_type;
rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
return 0;
}
chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
if (chg_type == BATT_NOT_CHG_VAL)
return POWER_SUPPLY_CHARGE_TYPE_NONE;
else if (chg_type == BATT_TAPER_CHG_VAL)
return POWER_SUPPLY_CHARGE_TYPE_TAPER;
else if (chg_type == BATT_FAST_CHG_VAL)
return POWER_SUPPLY_CHARGE_TYPE_FAST;
else if (chg_type == BATT_PRE_CHG_VAL)
return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
return POWER_SUPPLY_CHARGE_TYPE_NONE;
}
static int set_property_on_fg(struct smbchg_chip *chip,
enum power_supply_property prop, int val)
{
int rc;
union power_supply_propval ret = {0, };
if (!chip->bms_psy && chip->bms_psy_name)
chip->bms_psy =
power_supply_get_by_name((char *)chip->bms_psy_name);
if (!chip->bms_psy) {
pr_smb(PR_STATUS, "no bms psy found\n");
return -EINVAL;
}
ret.intval = val;
rc = chip->bms_psy->set_property(chip->bms_psy, prop, &ret);
if (rc)
pr_smb(PR_STATUS,
"bms psy does not allow updating prop %d rc = %d\n",
prop, rc);
return rc;
}
static int get_property_from_fg(struct smbchg_chip *chip,
enum power_supply_property prop, int *val)
{
int rc;
union power_supply_propval ret = {0, };
if (!chip->bms_psy && chip->bms_psy_name)
chip->bms_psy =
power_supply_get_by_name((char *)chip->bms_psy_name);
if (!chip->bms_psy) {
pr_smb(PR_STATUS, "no bms psy found\n");
return -EINVAL;
}
rc = chip->bms_psy->get_property(chip->bms_psy, prop, &ret);
if (rc) {
pr_smb(PR_STATUS,
"bms psy doesn't support reading prop %d rc = %d\n",
prop, rc);
return rc;
}
*val = ret.intval;
return rc;
}
static void check_usb_status(struct smbchg_chip *chip)
{
union power_supply_propval prop = {0,};
if (chip->usb_psy) {
chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_ONLINE, &prop);
/*
* if battery soc is 0% and usb_psy property online is true in
* normal mode(not power-off charging mode), set online to
* false to notify system to power off.
*/
if ((prop.intval == 1) && (!off_charge_flag) && chip->usb_present) {
power_supply_set_present(chip->usb_psy, false);
power_supply_set_online(chip->usb_psy, false);
chip->usb_present = false;
}
}
}
#define DEFAULT_BATT_CAPACITY 50
static int get_prop_batt_capacity(struct smbchg_chip *chip)
{
int capacity, rc;
if (chip->fake_battery_soc >= 0)
return chip->fake_battery_soc;
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
capacity = DEFAULT_BATT_CAPACITY;
}
if (capacity == 0) {
check_usb_status(chip);
}
return capacity;
}
/* parse the androidboot.mode, check whether it is power-off charging */
static int __init early_parse_off_charge_flag(char *p)
{
if (p) {
if (!strcmp(p, "charger"))
off_charge_flag = true;
}
return 0;
}
early_param("androidboot.mode", early_parse_off_charge_flag);
#define DEFAULT_BATT_TEMP 200
static int get_prop_batt_temp(struct smbchg_chip *chip)
{
int temp, rc;
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
temp = DEFAULT_BATT_TEMP;
}
return temp;
}
#define DEFAULT_BATT_CURRENT_NOW 0
static int get_prop_batt_current_now(struct smbchg_chip *chip)
{
int ua, rc;
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
ua = DEFAULT_BATT_CURRENT_NOW;
}
return ua;
}
#define DEFAULT_BATT_VOLTAGE_NOW 0
static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
{
int uv, rc;
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
uv = DEFAULT_BATT_VOLTAGE_NOW;
}
return uv;
}
#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000
static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip)
{
int uv, rc;
rc = get_property_from_fg(chip,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN;
}
return uv;
}
static int get_prop_batt_health(struct smbchg_chip *chip)
{
if (chip->batt_hot)
return POWER_SUPPLY_HEALTH_OVERHEAT;
else if (chip->batt_cold)
return POWER_SUPPLY_HEALTH_COLD;
else if (chip->batt_warm)
return POWER_SUPPLY_HEALTH_WARM;
else if (chip->batt_cool)
return POWER_SUPPLY_HEALTH_COOL;
else
return POWER_SUPPLY_HEALTH_GOOD;
}
/* add for healthd, as healthd do not have warm/cool */
static int get_batt_health(struct smbchg_chip *chip)
{
if (chip->batt_hot)
return POWER_SUPPLY_HEALTH_OVERHEAT;
else if (chip->batt_cold)
return POWER_SUPPLY_HEALTH_COLD;
else
return POWER_SUPPLY_HEALTH_GOOD;
}
static int get_prop_batt_profile(struct smbchg_chip *chip)
{
int profile_status, rc;
rc = get_property_from_fg(chip,
POWER_SUPPLY_PROP_PROFILE_STATUS, &profile_status);
if (rc) {
pr_smb(PR_STATUS, "Couldn't get profile status rc = %d\n", rc);
profile_status = 0;
}
return profile_status;
}
static const int usb_current_table[] = {
300,
400,
450,
475,
500,
550,
600,
650,
700,
900,
950,
1000,
1100,
1200,
1400,
1450,
1500,
1600,
1800,
1850,
1880,
1910,
1930,
1950,
1970,
2000,
2050,
2100,
2300,
2400,
2500,
3000
};
static const int dc_current_table[] = {
300,
400,
450,
475,
500,
550,
600,
650,
700,
900,
950,
1000,
1100,
1200,
1400,
1450,
1500,
1600,
1800,
1850,
1880,
1910,
1930,
1950,
1970,
2000,
};
static const int fcc_comp_table[] = {
250,
700,
900,
1200,
};
static int calc_thermal_limited_current(struct smbchg_chip *chip,
int current_ma)
{
int therm_ma;
if (chip->therm_lvl_sel > 0
&& chip->therm_lvl_sel < (chip->thermal_levels - 1)) {
/*
* consider thermal limit only when it is active and not at
* the highest level
*/
if (smbchg_is_hvdcp(chip))
therm_ma = (int)chip->thermal_mitigation_hvdcp[chip->therm_lvl_sel];
else
therm_ma = (int)chip->thermal_mitigation[chip->therm_lvl_sel];
if (therm_ma < current_ma) {
pr_smb(PR_STATUS,
"Limiting current due to thermal: %d mA",
therm_ma);
return therm_ma;
}
}
return current_ma;
}
#define CMD_CHG_REG 0x42
#define EN_BAT_CHG_BIT BIT(1)
static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
{
/* The en bit is configured active low */
return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
}
#define CMD_IL 0x40
#define USBIN_SUSPEND_BIT BIT(4)
#define CURRENT_100_MA 100
#define CURRENT_150_MA 150
#define CURRENT_500_MA 500
#define CURRENT_900_MA 900
#define CURRENT_1500_MA 1500
#define SUSPEND_CURRENT_MA 2
#define ICL_OVERRIDE_BIT BIT(2)
static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend)
{
int rc;
rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0);
if (rc < 0)
dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc);
return rc;
}
#define DCIN_SUSPEND_BIT BIT(3)
static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend)
{
int rc = 0;
rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0);
if (rc < 0)
dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc);
return rc;
}
#define IL_CFG 0xF2
#define DCIN_INPUT_MASK SMB_MASK(4, 0)
static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma)
{
int i;
u8 dc_cur_val;
for (i = ARRAY_SIZE(dc_current_table) - 1; i >= 0; i--) {
if (current_ma >= dc_current_table[i])
break;
}
if (i < 0) {
dev_err(chip->dev, "Cannot find %dma current_table\n",
current_ma);
return -EINVAL;
}
chip->dc_max_current_ma = dc_current_table[i];
dc_cur_val = i & DCIN_INPUT_MASK;
pr_smb(PR_STATUS, "dc current set to %d mA\n",
chip->dc_max_current_ma);
return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG,
DCIN_INPUT_MASK, dc_cur_val);
}
enum enable_reason {
/* userspace has suspended charging altogether */
REASON_USER = BIT(0),
/*
* this specific path has been suspended through the power supply
* framework
*/
REASON_POWER_SUPPLY = BIT(1),
/*
* the usb driver has suspended this path by setting a current limit
* of < 2MA
*/
REASON_USB = BIT(2),
/*
* when a wireless charger comes online,
* the dc path is suspended for a second
*/
REASON_WIRELESS = BIT(3),
/*
* the thermal daemon can suspend a charge path when the system
* temperature levels rise
*/
REASON_THERMAL = BIT(4),
/*
* an external OTG supply is being used, suspend charge path so the
* charger does not accidentally try to charge from the external supply.
*/
REASON_OTG = BIT(5),
/*
* the charger is very weak, do not draw any current from it
*/
REASON_WEAK_CHARGER = BIT(6),
};
enum battchg_enable_reason {
/* userspace has disabled battery charging */
REASON_BATTCHG_USER = BIT(0),
/* battery charging disabled while loading battery profiles */
REASON_BATTCHG_UNKNOWN_BATTERY = BIT(1),
};
static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
{
if (!chip->parallel.avail)
return NULL;
if (chip->parallel.psy)
return chip->parallel.psy;
chip->parallel.psy = power_supply_get_by_name("usb-parallel");
if (!chip->parallel.psy)
pr_smb(PR_STATUS, "parallel charger not found\n");
return chip->parallel.psy;
}
static void smbchg_usb_update_online_work(struct work_struct *work)
{
struct smbchg_chip *chip = container_of(work,
struct smbchg_chip,
usb_set_online_work);
bool user_enabled = (chip->usb_suspended & REASON_USER) == 0;
int online;
online = user_enabled && chip->usb_present && !chip->very_weak_charger;
mutex_lock(&chip->usb_set_online_lock);
if (chip->usb_online != online) {
pr_smb(PR_MISC, "setting usb psy online = %d\n", online);
power_supply_set_online(chip->usb_psy, online);
chip->usb_online = online;
}
mutex_unlock(&chip->usb_set_online_lock);
}
static bool smbchg_primary_usb_is_en(struct smbchg_chip *chip,
enum enable_reason reason)
{
bool enabled;
mutex_lock(&chip->usb_en_lock);
enabled = (chip->usb_suspended & reason) == 0;
mutex_unlock(&chip->usb_en_lock);
return enabled;
}
static bool smcghg_is_battchg_en(struct smbchg_chip *chip,
enum battchg_enable_reason reason)
{
bool enabled;
mutex_lock(&chip->battchg_disabled_lock);
enabled = !(chip->battchg_disabled & reason);
mutex_unlock(&chip->battchg_disabled_lock);
return enabled;
}
static int smbchg_battchg_en(struct smbchg_chip *chip, bool enable,
enum battchg_enable_reason reason, bool *changed)
{
int rc = 0, battchg_disabled;
pr_smb(PR_STATUS, "battchg %s, susp = %02x, en? = %d, reason = %02x\n",
chip->battchg_disabled == 0 ? "enabled" : "disabled",
chip->battchg_disabled, enable, reason);
mutex_lock(&chip->battchg_disabled_lock);
if (!enable)
battchg_disabled = chip->battchg_disabled | reason;
else
battchg_disabled = chip->battchg_disabled & (~reason);
/* avoid unnecessary spmi interactions if nothing changed */
/* avoid goto skip when enable charge but chip->battchg_disabled is 0 */
if ((!!battchg_disabled == !!chip->battchg_disabled)
&& chip->battchg_disabled) {
*changed = false;
goto out;
}
rc = smbchg_charging_en(chip, !battchg_disabled);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't configure batt chg: 0x%x rc = %d\n",
battchg_disabled, rc);
goto out;
}
*changed = true;
pr_smb(PR_STATUS, "batt charging %s, battchg_disabled = %02x\n",
battchg_disabled == 0 ? "enabled" : "disabled",
battchg_disabled);
out:
chip->battchg_disabled = battchg_disabled;
mutex_unlock(&chip->battchg_disabled_lock);
return rc;
}
static int smbchg_primary_usb_en(struct smbchg_chip *chip, bool enable,
enum enable_reason reason, bool *changed)
{
int rc = 0, suspended;
pr_smb(PR_STATUS, "usb %s, susp = %02x, en? = %d, reason = %02x\n",
chip->usb_suspended == 0 ? "enabled"
: "suspended", chip->usb_suspended, enable, reason);
mutex_lock(&chip->usb_en_lock);
if (!enable)
suspended = chip->usb_suspended | reason;
else
suspended = chip->usb_suspended & (~reason);
/* avoid unnecessary spmi interactions if nothing changed */
/* avoid goto skip when usb enable but chip->usb_suspended is 0 */
if ((!!suspended == !!chip->usb_suspended)
&& chip->usb_suspended) {
*changed = false;
goto out;
}
*changed = true;
rc = smbchg_usb_suspend(chip, suspended != 0);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set usb suspend: %d rc = %d\n",
suspended, rc);
goto out;
}
pr_smb(PR_STATUS, "usb charging %s, suspended = %02x\n",
suspended == 0 ? "enabled"
: "suspended", suspended);
out:
chip->usb_suspended = suspended;
mutex_unlock(&chip->usb_en_lock);
return rc;
}
static int smbchg_dc_en(struct smbchg_chip *chip, bool enable,
enum enable_reason reason)
{
int rc = 0, suspended;
pr_smb(PR_STATUS, "dc %s, susp = %02x, en? = %d, reason = %02x\n",
chip->dc_suspended == 0 ? "enabled"
: "suspended", chip->dc_suspended, enable, reason);
mutex_lock(&chip->dc_en_lock);
if (!enable)
suspended = chip->dc_suspended | reason;
else
suspended = chip->dc_suspended & ~reason;
/* avoid unnecessary spmi interactions if nothing changed */
if (!!suspended == !!chip->dc_suspended)
goto out;
rc = smbchg_dc_suspend(chip, suspended != 0);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set dc suspend: %d rc = %d\n",
suspended, rc);
goto out;
}
if (chip->dc_psy_type != -EINVAL && chip->psy_registered)
power_supply_changed(&chip->dc_psy);
pr_smb(PR_STATUS, "dc charging %s, suspended = %02x\n",
suspended == 0 ? "enabled"
: "suspended", suspended);
out:
chip->dc_suspended = suspended;
mutex_unlock(&chip->dc_en_lock);
return rc;
}
#define CHGPTH_CFG 0xF4
#define CFG_USB_2_3_SEL_BIT BIT(7)
#define CFG_USB_2 0
#define CFG_USB_3 BIT(7)
#define USBIN_INPUT_MASK SMB_MASK(4, 0)
#define USBIN_MODE_CHG_BIT BIT(0)
#define USBIN_LIMITED_MODE 0
#define USBIN_HC_MODE BIT(0)
#define USB51_MODE_BIT BIT(1)
#define USB51_100MA 0
#define USB51_500MA BIT(1)
static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip,
int current_ma)
{
int i, rc;
u8 usb_cur_val;
for (i = ARRAY_SIZE(usb_current_table) - 1; i >= 0; i--) {
if (current_ma >= usb_current_table[i])
break;
}
if (i < 0) {
dev_err(chip->dev,
"Cannot find %dma current_table using %d\n",
current_ma, CURRENT_150_MA);
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
CFG_USB_2_3_SEL_BIT, CFG_USB_3);
rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
USBIN_LIMITED_MODE | USB51_100MA);
if (rc < 0)
dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
CURRENT_150_MA, rc);
else
chip->usb_max_current_ma = 150;
return rc;
}
usb_cur_val = i & USBIN_INPUT_MASK;
rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG,
USBIN_INPUT_MASK, usb_cur_val);
if (rc < 0) {
dev_err(chip->dev, "cannot write to config c rc = %d\n", rc);
return rc;
}
rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
USBIN_MODE_CHG_BIT, USBIN_HC_MODE);
if (rc < 0)
dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc);
chip->usb_max_current_ma = usb_current_table[i];
return rc;
}
/* if APSD results are used
* if SDP is detected it will look at 500mA setting
* if set it will draw 500mA
* if unset it will draw 100mA
* if CDP/DCP it will look at 0x0C setting
* i.e. values in 0x41[1, 0] does not matter
*/
static int smbchg_set_usb_current_max(struct smbchg_chip *chip,
int current_ma)
{
int rc = 0;
bool changed;
enum power_supply_type usb_supply_type;
char *usb_type_name = "null";
if (!chip->batt_present) {
pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n",
current_ma);
return 0;
}
pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma);
if (current_ma == SUSPEND_CURRENT_MA) {
/* suspend the usb if current set to 2mA */
rc = smbchg_primary_usb_en(chip, false, REASON_USB, &changed);
chip->usb_max_current_ma = 0;
goto out;
} else {
rc = smbchg_primary_usb_en(chip, true, REASON_USB, &changed);
}
if (chip->low_icl_wa_on) {
chip->usb_max_current_ma = current_ma;
pr_smb(PR_STATUS,
"low_icl_wa on, ignoring the usb current setting\n");
goto out;
}
read_usb_type(chip, &usb_type_name, &usb_supply_type);
switch (usb_supply_type) {
case POWER_SUPPLY_TYPE_USB:
if (current_ma < CURRENT_150_MA) {
/* force 100mA */
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
CFG_USB_2_3_SEL_BIT, CFG_USB_2);
if (rc < 0) {
pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
goto out;
}
rc = smbchg_masked_write(chip,
chip->usb_chgpth_base + CMD_IL,
USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
USBIN_LIMITED_MODE | USB51_100MA);
if (rc < 0) {
pr_err("Couldn't set CMD_IL rc = %d\n", rc);
goto out;
}
chip->usb_max_current_ma = 100;
}
/* specific current values */
if (current_ma == CURRENT_150_MA) {
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
CFG_USB_2_3_SEL_BIT, CFG_USB_3);
if (rc < 0) {
pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
goto out;
}
rc = smbchg_masked_write(chip,
chip->usb_chgpth_base + CMD_IL,
USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
USBIN_LIMITED_MODE | USB51_100MA);
if (rc < 0) {
pr_err("Couldn't set CMD_IL rc = %d\n", rc);
goto out;
}
chip->usb_max_current_ma = 150;
}
if (current_ma == CURRENT_500_MA) {
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
CFG_USB_2_3_SEL_BIT, CFG_USB_2);
if (rc < 0) {
pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
goto out;
}
rc = smbchg_masked_write(chip,
chip->usb_chgpth_base + CMD_IL,
USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
USBIN_LIMITED_MODE | USB51_500MA);
if (rc < 0) {
pr_err("Couldn't set CMD_IL rc = %d\n", rc);
goto out;
}
chip->usb_max_current_ma = 500;
}
/*
* Remove the code of 900ma, as angler do not support USB3.0.
* MacBook which has typec port is detected as SDP, and it is
* also a typec port using medium current mode (22k pull up resistance)
* so set 1.5A input current according to typec protocol, 1.5A is
* above parallel charging threshold, enable parallel charging, set
* half of 1.5A (0.7A) for pmi8994 and smb1351, so if current_ma
* is above 500ma, set HC(high current) mode for pmi8994.
* Set HC mode also for SDP 500mA, if not set, AICL will not run, so
* compass compensation is not accurate with SDP 500mA, it is because
* we use AICL to calculate compass compensation, and AICL is 1.5A
* in default.
*/
rc = smbchg_set_high_usb_chg_current(chip, current_ma);
if (rc < 0) {
pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
goto out;
}
break;
case POWER_SUPPLY_TYPE_USB_CDP:
if (current_ma < CURRENT_1500_MA) {
/* use override for CDP */
rc = smbchg_masked_write(chip,
chip->usb_chgpth_base + CMD_IL,
ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
if (rc < 0)
pr_err("Couldn't set override rc = %d\n", rc);
}
/* fall through */
default:
rc = smbchg_set_high_usb_chg_current(chip, current_ma);
if (rc < 0)
pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
break;
}
out:
pr_smb(PR_STATUS, "usb type = %s current set to %d mA\n",
usb_type_name, chip->usb_max_current_ma);
return rc;
}
#define USBIN_HVDCP_STS 0x0C
#define USBIN_HVDCP_SEL_BIT BIT(4)
#define USBIN_HVDCP_SEL_9V_BIT BIT(1)
static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg,
chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
return 0;
}
if ((reg & USBIN_HVDCP_SEL_BIT) && (reg & USBIN_HVDCP_SEL_9V_BIT))
return chip->parallel.min_9v_current_thr_ma;
return chip->parallel.min_current_thr_ma;
}
#define ICL_STS_1_REG 0x7
#define ICL_STS_2_REG 0x9
#define ICL_STS_MASK 0x1F
#define AICL_SUSP_BIT BIT(6)
#define AICL_STS_BIT BIT(5)
#define USBIN_SUSPEND_STS_BIT BIT(3)
#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1)
#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0)
/* change parallel reenable timer to 2.5 seconds */
#define PARALLEL_REENABLE_TIMER_MS 2500
static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip)
{
int min_current_thr_ma, rc, type;
ktime_t kt_since_last_disable;
u8 reg;
enum typec_current_mode current_mode = TYPEC_CURRENT_MODE_DEFAULT;
if (!smbchg_parallel_en || !chip->parallel_charger_detected) {
pr_smb(PR_STATUS, "Parallel charging not enabled\n");
return false;
}
kt_since_last_disable = ktime_sub(ktime_get_boottime(),
chip->parallel.last_disabled);
if (chip->parallel.current_max_ma == 0
&& chip->parallel.enabled_once
&& ktime_to_ms(kt_since_last_disable)
< PARALLEL_REENABLE_TIMER_MS) {
pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n",
ktime_to_ms(kt_since_last_disable));
return false;
}
/* Skip charge type check when vfloat_mv isn't set as cfg_vfloat_mv */
if ((get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST)
&& (chip->vfloat_mv == chip->cfg_vfloat_mv)) {
pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
return false;
}
if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
pr_smb(PR_STATUS, "JEITA active, skipping\n");
return false;
}
rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
return false;
}
type = get_type(reg);
if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) {
pr_smb(PR_STATUS, "CDP adapter, skipping\n");
return false;
}
current_mode = typec_current_mode_detect();
if ((get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB)
&& ((current_mode == TYPEC_CURRENT_MODE_DEFAULT)
|| (current_mode == TYPEC_CURRENT_MODE_UNSPPORTED))) {
pr_smb(PR_STATUS, "SDP adapter, skipping\n");
return false;
}
rc = smbchg_read(chip, &reg,
chip->usb_chgpth_base + ICL_STS_2_REG, 1);
if (rc) {
dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc);
return false;
}
/*
* If USBIN is suspended or not the active power source, do not enable
* parallel charging. The device may be charging off of DCIN.
*/
if (!!(reg & USBIN_SUSPEND_STS_BIT) ||
!(reg & USBIN_ACTIVE_PWR_SRC_BIT)) {
pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg);
return false;
}
min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
if (min_current_thr_ma <= 0) {
pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
min_current_thr_ma);
return false;
}
if (chip->usb_tl_current_ma < min_current_thr_ma) {
pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n",
chip->usb_tl_current_ma, min_current_thr_ma);
return false;
}
return true;
}
#define FCC_CFG 0xF2
#define FCC_500MA_VAL 0x4
#define FCC_MASK SMB_MASK(4, 0)
static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,
int current_ma)
{
int i, rc;
u8 cur_val;
/* the fcc enumerations are the same as the usb currents */
for (i = ARRAY_SIZE(usb_current_table) - 1; i >= 0; i--) {
if (current_ma >= usb_current_table[i])
break;
}
if (i < 0) {
dev_err(chip->dev,
"Cannot find %dma current_table using %d\n",
current_ma, CURRENT_500_MA);
rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
FCC_MASK,
FCC_500MA_VAL);
if (rc < 0)
dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
CURRENT_500_MA, rc);
else
chip->fastchg_current_ma = 500;
return rc;
}
cur_val = i & FCC_MASK;
rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
FCC_MASK, cur_val);
if (rc < 0) {
dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc);
return rc;
}
pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n",
current_ma, usb_current_table[cur_val]);
chip->fastchg_current_ma = usb_current_table[cur_val];
return rc;
}
static int smbchg_set_fastchg_current(struct smbchg_chip *chip,
int current_ma)
{
int rc = 0;
mutex_lock(&chip->fcc_lock);
if (chip->sw_esr_pulse_en)
current_ma = 300;
/* If the requested FCC is same, do not configure it again */
if (current_ma == chip->fastchg_current_ma) {
pr_smb(PR_STATUS, "not configuring FCC current: %d FCC: %d\n",
current_ma, chip->fastchg_current_ma);
goto out;
}
rc = smbchg_set_fastchg_current_raw(chip, current_ma);
out:
mutex_unlock(&chip->fcc_lock);
return rc;
}
static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
union power_supply_propval pval = {0, };
if (!parallel_psy || !chip->parallel_charger_detected)
return 0;
pval.intval = en;
return parallel_psy->set_property(parallel_psy,
POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
}
static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en)
{
int rc;
chip->sw_esr_pulse_en = en;
rc = smbchg_set_fastchg_current(chip, chip->target_fastchg_current_ma);
if (rc)
return rc;
rc = smbchg_parallel_usb_charging_en(chip, !en);
return rc;
}
#define USB_AICL_CFG 0xF3
#define AICL_EN_BIT BIT(2)
static void smbchg_rerun_aicl(struct smbchg_chip *chip)
{
pr_smb(PR_STATUS, "Rerunning AICL...\n");
smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
AICL_EN_BIT, 0);
/* Add a delay so that AICL successfully clears */
msleep(50);
smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
AICL_EN_BIT, AICL_EN_BIT);
}
static void taper_irq_en(struct smbchg_chip *chip, bool en)
{
mutex_lock(&chip->taper_irq_lock);
if (en != chip->taper_irq_enabled) {
if (en) {
enable_irq(chip->taper_irq);
enable_irq_wake(chip->taper_irq);
} else {
disable_irq_wake(chip->taper_irq);
disable_irq_nosync(chip->taper_irq);
}
chip->taper_irq_enabled = en;
}
mutex_unlock(&chip->taper_irq_lock);
}
static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
if (!parallel_psy || !chip->parallel_charger_detected)
return;
pr_smb(PR_STATUS, "disabling parallel charger\n");
chip->parallel.last_disabled = ktime_get_boottime();
taper_irq_en(chip, false);
chip->parallel.initial_aicl_ma = 0;
chip->parallel.current_max_ma = 0;
power_supply_set_current_limit(parallel_psy,
SUSPEND_CURRENT_MA * 1000);
power_supply_set_present(parallel_psy, false);
if (chip->batt_warm)
chip->target_fastchg_current_ma = chip->warm_current_ma;
else if (chip->batt_cool)
chip->target_fastchg_current_ma = chip->cool_current_ma;
else
chip->target_fastchg_current_ma = chip->cfg_fastchg_current_ma;
smbchg_set_fastchg_current(chip, chip->target_fastchg_current_ma);
chip->usb_tl_current_ma =
calc_thermal_limited_current(chip, chip->usb_target_current_ma);
smbchg_set_usb_current_max(chip, chip->usb_tl_current_ma);
smbchg_rerun_aicl(chip);
}
#define PARALLEL_TAPER_MAX_TRIES 3
#define PARALLEL_FCC_PERCENT_REDUCTION 75
#define MINIMUM_PARALLEL_FCC_MA 500
#define CHG_ERROR_BIT BIT(0)
#define BAT_TAPER_MODE_BIT BIT(6)
static void smbchg_parallel_usb_taper(struct smbchg_chip *chip)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
union power_supply_propval pval = {0, };
int parallel_fcc_ma, tries = 0;
u8 reg = 0;
if (!parallel_psy || !chip->parallel_charger_detected)
return;
smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
try_again:
mutex_lock(&chip->parallel.lock);
if (chip->parallel.current_max_ma == 0) {
pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
goto done;
}
parallel_psy->get_property(parallel_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
tries += 1;
parallel_fcc_ma = pval.intval / 1000;
pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n",
tries, parallel_fcc_ma);
if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA
|| tries > PARALLEL_TAPER_MAX_TRIES) {
smbchg_parallel_usb_disable(chip);
goto done;
}
pval.intval = ((parallel_fcc_ma
* PARALLEL_FCC_PERCENT_REDUCTION) / 100);
pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n",
pval.intval);
/* Change it to uA */
pval.intval *= 1000;
parallel_psy->set_property(parallel_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
/*
* sleep here for 100 ms in order to make sure the charger has a chance
* to go back into constant current charging
*/
mutex_unlock(&chip->parallel.lock);
msleep(100);
mutex_lock(&chip->parallel.lock);
if (chip->parallel.current_max_ma == 0) {
pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
goto done;
}
smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
if (reg & BAT_TAPER_MODE_BIT) {
mutex_unlock(&chip->parallel.lock);
goto try_again;
}
taper_irq_en(chip, true);
done:
mutex_unlock(&chip->parallel.lock);
smbchg_relax(chip, PM_PARALLEL_TAPER);
}
static bool smbchg_is_aicl_complete(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg,
chip->usb_chgpth_base + ICL_STS_1_REG, 1);
if (rc) {
dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
return true;
}
return (reg & AICL_STS_BIT) != 0;
}
static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip)
{
int rc;
u8 reg;
rc = smbchg_read(chip, &reg,
chip->usb_chgpth_base + ICL_STS_1_REG, 1);
if (rc) {
dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
return 0;
}
if (reg & AICL_SUSP_BIT) {
pr_warn("AICL suspended: %02x\n", reg);
return 0;
}
reg &= ICL_STS_MASK;
if (reg >= ARRAY_SIZE(usb_current_table)) {
pr_warn("invalid AICL value: %02x\n", reg);
return 0;
}
return usb_current_table[reg];
}
#define PARALLEL_CHG_THRESHOLD_CURRENT 1800
static void smbchg_parallel_usb_enable(struct smbchg_chip *chip)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
union power_supply_propval pval = {0, };
int current_limit_ma, parallel_cl_ma, total_current_ma;
int new_parallel_cl_ma, min_current_thr_ma, rc;
int batt_voltage_mv;
u8 reg;
if (!parallel_psy || !chip->parallel_charger_detected)
return;
pr_smb(PR_STATUS, "Attempting to enable parallel charger\n");
/* Suspend the parallel charger if the charging current is < 1800 mA */
if (chip->cfg_fastchg_current_ma < PARALLEL_CHG_THRESHOLD_CURRENT) {
pr_smb(PR_STATUS, "suspend parallel charger as FCC is %d\n",
chip->cfg_fastchg_current_ma);
goto disable_parallel;
}
min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
if (min_current_thr_ma <= 0) {
pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
min_current_thr_ma);
goto disable_parallel;
}
current_limit_ma = smbchg_get_aicl_level_ma(chip);
if (current_limit_ma <= 0)
goto disable_parallel;
if (chip->parallel.initial_aicl_ma == 0) {
if (current_limit_ma < min_current_thr_ma) {
pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n",
current_limit_ma, min_current_thr_ma);
goto disable_parallel;
}
chip->parallel.initial_aicl_ma = current_limit_ma;
}
/*
* Use the previous set current from the parallel charger.
* Treat 2mA as 0 because that is the suspend current setting
*/
parallel_cl_ma = chip->parallel.current_max_ma;
if (parallel_cl_ma <= SUSPEND_CURRENT_MA)
parallel_cl_ma = 0;
/*
* Set the parallel charge path's input current limit (ICL)
* to the total current / 2
*/
total_current_ma = current_limit_ma + parallel_cl_ma;
if (total_current_ma < chip->parallel.initial_aicl_ma
- chip->parallel.allowed_lowering_ma) {
pr_smb(PR_STATUS,
"Too little total current : %d (%d + %d) < %d - %d\n",
total_current_ma,
current_limit_ma, parallel_cl_ma,
chip->parallel.initial_aicl_ma,
chip->parallel.allowed_lowering_ma);
goto disable_parallel;
}
rc = power_supply_set_voltage_limit(parallel_psy, chip->vfloat_mv + 50);
if (rc) {
dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
rc);
goto disable_parallel;
}
chip->target_fastchg_current_ma = chip->cfg_fastchg_current_ma / 2;
/*
* When the batt voltage is above the current_stage_thr_mv,
* limit parallel charge current to 0.7C, or set a lower
* vfloat_mv and call the work to check charge current. It
* will adjust the vfloat_mv back to the normal value with
* the conditions satisfied.
*/
if (!chip->parallel_voltage_checked && chip->current_stage_thr_mv > 0) {
rc = smbchg_read(chip, &reg,
chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read hvdcp status rc = %d\n", rc);
goto disable_parallel;
}
if ((typec_current_mode_detect() == TYPEC_CURRENT_MODE_HIGH)
|| (reg & USBIN_HVDCP_SEL_BIT)) {
set_property_on_fg(chip,
POWER_SUPPLY_PROP_UPDATE_NOW, 1);
batt_voltage_mv =
get_prop_batt_voltage_now(chip) / 1000;
if (batt_voltage_mv > chip->current_stage_thr_mv
- chip->current_stage_delta_mv) {
chip->parallel_current_limited = 1;
} else {
smbchg_float_voltage_set(chip,
chip->current_stage_thr_mv);
schedule_delayed_work(&chip->current_stage_work,
msecs_to_jiffies(STAGE_WORK_DELAY_MS));
}
}
chip->parallel_voltage_checked = 1;
}
if (chip->parallel_current_limited)
chip->target_fastchg_current_ma = chip->current_stage_ma / 2;
smbchg_set_fastchg_current(chip, chip->target_fastchg_current_ma);
pval.intval = chip->target_fastchg_current_ma * 1000;
parallel_psy->set_property(parallel_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
chip->parallel.enabled_once = true;
new_parallel_cl_ma = total_current_ma / 2;
if (new_parallel_cl_ma == parallel_cl_ma) {
pr_smb(PR_STATUS,
"AICL at %d, old ICL: %d new ICL: %d, skipping\n",
current_limit_ma, parallel_cl_ma, new_parallel_cl_ma);
return;
} else {
pr_smb(PR_STATUS, "AICL at %d, old ICL: %d new ICL: %d\n",
current_limit_ma, parallel_cl_ma, new_parallel_cl_ma);
}
taper_irq_en(chip, true);
chip->parallel.current_max_ma = new_parallel_cl_ma;
power_supply_set_present(parallel_psy, true);
smbchg_set_usb_current_max(chip, chip->parallel.current_max_ma);
power_supply_set_current_limit(parallel_psy,
chip->parallel.current_max_ma * 1000);
return;
disable_parallel:
if (chip->parallel.current_max_ma != 0) {
pr_smb(PR_STATUS, "disabling parallel charger\n");
smbchg_parallel_usb_disable(chip);
} else if (chip->cfg_fastchg_current_ma !=
chip->target_fastchg_current_ma) {
/* There is a possibility that parallel charging is enabled
* and a weak charger is connected, AICL result will be
* lower than the min_current_thr_ma. In those cases, we
* should fall back to configure the FCC of main charger.
*/
rc = smbchg_set_fastchg_current(chip,
chip->cfg_fastchg_current_ma);
if (rc)
pr_err("Couldn't set fastchg current rc: %d\n",
rc);
else
chip->target_fastchg_current_ma =
chip->cfg_fastchg_current_ma;
}
}
static void smbchg_parallel_usb_en_work(struct work_struct *work)
{
struct smbchg_chip *chip = container_of(work,
struct smbchg_chip,
parallel_en_work.work);
smbchg_relax(chip, PM_PARALLEL_CHECK);
mutex_lock(&chip->parallel.lock);
if (smbchg_is_parallel_usb_ok(chip)) {
smbchg_parallel_usb_enable(chip);
} else if (chip->parallel.current_max_ma != 0) {
pr_smb(PR_STATUS, "parallel charging unavailable\n");
smbchg_parallel_usb_disable(chip);
}
mutex_unlock(&chip->parallel.lock);
}
#define PARALLEL_CHARGER_EN_DELAY_MS 3500
static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
if (!parallel_psy || !chip->parallel_charger_detected)
return;
mutex_lock(&chip->parallel.lock);
if (smbchg_is_parallel_usb_ok(chip)) {
smbchg_stay_awake(chip, PM_PARALLEL_CHECK);
schedule_delayed_work(
&chip->parallel_en_work,
msecs_to_jiffies(PARALLEL_CHARGER_EN_DELAY_MS));
} else if (chip->parallel.current_max_ma != 0) {
pr_smb(PR_STATUS, "parallel charging unavailable\n");
smbchg_parallel_usb_disable(chip);
}
mutex_unlock(&chip->parallel.lock);
}
static int smbchg_usb_en(struct smbchg_chip *chip, bool enable,
enum enable_reason reason)
{
bool changed = false;
int rc = smbchg_primary_usb_en(chip, enable, reason, &changed);
if (changed)
smbchg_parallel_usb_check_ok(chip);
return rc;
}
static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip,
int current_ma)
{
int rc = 0;
mutex_lock(&chip->parallel.lock);
pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma);
chip->cfg_fastchg_current_ma = current_ma;
if (smbchg_is_parallel_usb_ok(chip)) {
smbchg_parallel_usb_enable(chip);
} else {
if (chip->parallel.current_max_ma != 0) {
/*
* If parallel charging is not available, disable it.
* FCC for main charger will be configured in that.
*/
pr_smb(PR_STATUS, "parallel charging unavailable\n");
smbchg_parallel_usb_disable(chip);
goto out;
}
rc = smbchg_set_fastchg_current(chip,
chip->cfg_fastchg_current_ma);
if (rc)
pr_err("Couldn't set fastchg current rc: %d\n",
rc);
}
out:
mutex_unlock(&chip->parallel.lock);
return rc;
}
static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip,
struct ilim_map *map, int uv)
{
int i;
struct ilim_entry *ret = &(chip->wipower_default.entries[0]);
for (i = 0; i < map->num; i++) {
if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv,
uv))
ret = &map->entries[i];
}
return ret;
}
static int ilim_ma_table[] = {
300,
400,
450,
475,
500,
550,
600,
650,
700,
900,
950,
1000,
1100,
1200,
1400,
1450,
1500,
1600,
1800,
1850,
1880,
1910,
1930,
1950,
1970,
2000,
};
#define ZIN_ICL_PT 0xFC
#define ZIN_ICL_LV 0xFD
#define ZIN_ICL_HV 0xFE
#define ZIN_ICL_MASK SMB_MASK(4, 0)
static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma)
{
int i, rc;
for (i = ARRAY_SIZE(ilim_ma_table) - 1; i >= 0; i--) {
if (ma >= ilim_ma_table[i])
break;
}
if (i < 0)
i = 0;
rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset,
ZIN_ICL_MASK, i);
if (rc)
dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n",
offset, i, rc);
return rc;
}
static int smbchg_wipower_ilim_config(struct smbchg_chip *chip,
struct ilim_entry *ilim)
{
int rc = 0;
if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) {
rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma);
if (rc)
dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
ZIN_ICL_PT, ilim->icl_pt_ma, rc);
else
chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
}
if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) {
rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma);
if (rc)
dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
ZIN_ICL_LV, ilim->icl_lv_ma, rc);
else
chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
}
if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) {
rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma);
if (rc)
dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
ZIN_ICL_HV, ilim->icl_hv_ma, rc);
else
chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
}
return rc;
}
static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx);
static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip,
struct ilim_entry *ilim)
{
int rc;
if (ilim->vmin_uv == chip->current_ilim.vmin_uv
&& ilim->vmax_uv == chip->current_ilim.vmax_uv)
return 0;
chip->param.channel = DCIN;
chip->param.btm_ctx = chip;
if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS)
wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS;
if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S)
wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S;
chip->param.timer_interval = wipower_dcin_interval;
chip->param.threshold_notification = &btm_notify_dcin;
chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv;
chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv;
chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param);
if (rc) {
dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n",
rc);
} else {
chip->current_ilim.vmin_uv = ilim->vmin_uv;
chip->current_ilim.vmax_uv = ilim->vmax_uv;
pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n",
ilim->vmin_uv, ilim->vmax_uv,
ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
}
return rc;
}
static int smbchg_wipower_icl_configure(struct smbchg_chip *chip,
int dcin_uv, bool div2)
{
int rc = 0;
struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt;
struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv);
rc = smbchg_wipower_ilim_config(chip, ilim);
if (rc) {
dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
rc, dcin_uv, div2,
ilim->vmin_uv, ilim->vmax_uv,
ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
return rc;
}
rc = smbchg_wipower_dcin_btm_configure(chip, ilim);
if (rc) {
dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
rc, dcin_uv, div2,
ilim->vmin_uv, ilim->vmax_uv,
ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
return rc;
}
chip->wipower_configured = true;
return 0;
}
static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip)
{
int rc;
struct ilim_entry *ilim = &(chip->wipower_default.entries[0]);
if (!chip->wipower_configured)
return;
rc = smbchg_wipower_ilim_config(chip, ilim);
if (rc)
dev_err(chip->dev, "Couldn't config default ilim rc = %d\n",
rc);
rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev);
if (rc)
dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n",
rc);
chip->wipower_configured = false;
chip->current_ilim.vmin_uv = 0;
chip->current_ilim.vmax_uv = 0;
chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
pr_smb(PR_WIPOWER, "De config btm\n");
}
#define FV_STS 0x0C
#define DIV2_ACTIVE BIT(7)
static void __smbchg_wipower_check(struct smbchg_chip *chip)
{
int chg_type;
bool usb_present, dc_present;
int rc;
int dcin_uv;
bool div2;
struct qpnp_vadc_result adc_result;
u8 reg;
if (!wipower_dyn_icl_en) {
smbchg_wipower_icl_deconfigure(chip);
return;
}
chg_type = get_prop_charge_type(chip);
usb_present = is_usb_present(chip);
dc_present = is_dc_present(chip);
if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE
&& !usb_present
&& dc_present
&& chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) {
rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result);
if (rc) {
pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
return;
}
dcin_uv = adc_result.physical;
/* check div_by_2 */
rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS, 1);
if (rc) {
pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
return;
}
div2 = !!(reg & DIV2_ACTIVE);
pr_smb(PR_WIPOWER,
"config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n",
chg_type, usb_present, dc_present, dcin_uv,
adc_result.adc_code, div2);
smbchg_wipower_icl_configure(chip, dcin_uv, div2);
} else {
pr_smb(PR_WIPOWER,
"deconfig ICL chg_type = %d usb = %d dc = %d\n",
chg_type, usb_present, dc_present);
smbchg_wipower_icl_deconfigure(chip);
}
}
static void smbchg_wipower_check(struct smbchg_chip *chip)
{
if (!chip->wipower_dyn_icl_avail)
return;
mutex_lock(&chip->wipower_config);
__smbchg_wipower_check(chip);
mutex_unlock(&chip->wipower_config);
}
static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx)
{
struct smbchg_chip *chip = ctx;
mutex_lock(&chip->wipower_config);
pr_smb(PR_WIPOWER, "%s state\n",
state == ADC_TM_LOW_STATE ? "low" : "high");
chip->current_ilim.vmin_uv = 0;
chip->current_ilim.vmax_uv = 0;
__smbchg_wipower_check(chip);
mutex_unlock(&chip->wipower_config);
}
static int force_dcin_icl_write(void *data, u64 val)
{
struct smbchg_chip *chip = data;
smbchg_wipower_check(chip);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL,
force_dcin_icl_write, "0x%02llx\n");
/*
* set the dc charge path's maximum allowed current draw
* that may be limited by the system's thermal level
*/
static int smbchg_set_thermal_limited_dc_current_max(struct smbchg_chip *chip,
int current_ma)
{
current_ma = calc_thermal_limited_current(chip, current_ma);
return smbchg_set_dc_current_max(chip, current_ma);
}
/*
* get input current according to type-c protocol
*/
#define FLOAT_CURRENT 1000
#define MEDIUM_CURRENT 1500
#define TYPEC_HIGH_CURRENT 3000
static int get_typec_input_current(void)
{
int typec_current = 0;
enum typec_current_mode current_mode = TYPEC_CURRENT_MODE_DEFAULT;
current_mode = typec_current_mode_detect();
switch (current_mode) {
case TYPEC_CURRENT_MODE_HIGH:
typec_current = TYPEC_HIGH_CURRENT;
break;
case TYPEC_CURRENT_MODE_MID:
typec_current = MEDIUM_CURRENT;
break;
case TYPEC_CURRENT_MODE_DEFAULT:
default:
typec_current = CURRENT_500_MA;
break;
}
return typec_current;
}
/*
* determine the input current according to BC1.2 and type-c protocol
* typeC charger/usb with 10k ohm pull-up resistance is 3A; hvdcp is 1.8A;
* BC1.2 DCP with D+/- shorted is 1.5A; SDP 0.5A; floated charger is 1A
*/
static int determine_target_input_current(int current_ma)
{
int target_current = 0, typec_current = 0;
typec_current = get_typec_input_current();
if (DEFAULT_USB_MA >= current_ma) {
/* for usb suspend or 100ma */
target_current = current_ma;
} else {
/* typec high and medium current mode has high priority*/
target_current = max(typec_current, current_ma);
}
pr_info("target_current = %d\n", target_current);
return target_current;
}
static void resume_to_normal_charge_work(struct work_struct *work)
{
int rc, aicl_ma;
struct smbchg_chip *chip = container_of(work,
struct smbchg_chip,
resume_to_normal_work);
if (chip->max_input_current_ma <= CURRENT_500_MA)
return;
aicl_ma = smbchg_get_aicl_level_ma(chip);
rc = smbchg_set_usb_current_max(chip, chip->max_input_current_ma);
if (rc) {
pr_err("Failed to set usb current max: %d\n", rc);
return;
}
if (chip->usb_max_current_ma > aicl_ma && smbchg_is_aicl_complete(chip))
smbchg_rerun_aicl(chip);
smbchg_parallel_usb_check_ok(chip);
}
/*
* set the usb charge path's maximum allowed current draw
* that may be limited by the system's thermal level
*/
static int smbchg_set_thermal_limited_usb_current_max(struct smbchg_chip *chip,
int current_ma)
{
int rc, aicl_ma;
int target_ma = 0;
enum power_supply_type usb_supply_type;
char *usb_type_name = "null";
aicl_ma = smbchg_get_aicl_level_ma(chip);
/* determine the input current by typec protocol and BC1.2 */
target_ma = determine_target_input_current(current_ma);
read_usb_type(chip, &usb_type_name, &usb_supply_type);
if (usb_supply_type == POWER_SUPPLY_TYPE_USB) {
/*
* set ICL_OVERRIDE_BIT when use typec charger (D+/- are floated)
* or floated charger (floated charger is detected as SDP by pmi8994)
* or SDP USB.
*/
rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
return rc;
}
}
chip->usb_tl_current_ma =
calc_thermal_limited_current(chip, target_ma);
/* save target input current to chip->max_input_current_ma,
* when charging recover from warm/cool to normal,
* use this input current to resume high current charging
*/
chip->max_input_current_ma = chip->usb_tl_current_ma;
rc = smbchg_set_usb_current_max(chip, chip->usb_tl_current_ma);
if (rc) {
pr_err("Failed to set usb current max: %d\n", rc);
return rc;
}
pr_smb(PR_STATUS, "AICL = %d, ICL = %d\n",
aicl_ma, chip->usb_max_current_ma);
if (chip->usb_max_current_ma > aicl_ma && smbchg_is_aicl_complete(chip))
smbchg_rerun_aicl(chip);
smbchg_parallel_usb_check_ok(chip);
return rc;
}
static int smbchg_system_temp_level_set(struct smbchg_chip *chip,
int lvl_sel)
{
int rc = 0;
int prev_therm_lvl;
int therm_ma, min_current_thr_ma;
struct power_supply *parallel_psy = get_parallel_psy(chip);
if (!chip->thermal_mitigation) {
dev_err(chip->dev, "Thermal mitigation not supported\n");
return -EINVAL;
}
if (!chip->thermal_mitigation_hvdcp) {
dev_err(chip->dev, "HVDCP Thermal mitigation not supported\n");
return -EINVAL;
}
if (lvl_sel < 0) {
dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel);
return -EINVAL;
}
if (lvl_sel >= chip->thermal_levels) {
dev_err(chip->dev, "Unsupported level selected %d forcing %d\n",
lvl_sel, chip->thermal_levels - 1);
lvl_sel = chip->thermal_levels - 1;
}
if (lvl_sel == chip->therm_lvl_sel)
return 0;
mutex_lock(&chip->current_change_lock);
prev_therm_lvl = chip->therm_lvl_sel;
chip->therm_lvl_sel = lvl_sel;
if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
/*
* Disable charging if highest value selected by
* setting the DC and USB path in suspend
*/
rc = smbchg_dc_en(chip, false, REASON_THERMAL);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set dc suspend rc %d\n", rc);
goto out;
}
rc = smbchg_usb_en(chip, false, REASON_THERMAL);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set usb suspend rc %d\n", rc);
goto out;
}
goto out;
}
/*
* If thermal-engine set therm_ma which is above min_current_thr_ma,
* reset chip->parallel.initial_aicl_ma and chip->parallel.current_max_ma
* to 0. if parallel charging is running, use new input
* current to re-run aicl to enable new parallel charging.
* if therm_ma is below min_current_thr_ma, disable parallel charging.
*/
if (chip->parallel.current_max_ma !=0) {
min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
if (min_current_thr_ma <= 0) {
pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
min_current_thr_ma);
goto out;
}
if (smbchg_is_hvdcp(chip))
therm_ma = (int)chip->thermal_mitigation_hvdcp[chip->therm_lvl_sel];
else
therm_ma = (int)chip->thermal_mitigation[chip->therm_lvl_sel];
if (therm_ma < min_current_thr_ma)
smbchg_parallel_usb_disable(chip);
else {
chip->parallel.initial_aicl_ma = 0;
chip->parallel.current_max_ma = 0;
if (!parallel_psy || !chip->parallel_charger_detected)
goto out;
power_supply_set_present(parallel_psy, false);
}
}
rc = smbchg_set_thermal_limited_usb_current_max(chip,
chip->usb_target_current_ma);
rc = smbchg_set_thermal_limited_dc_current_max(chip,
chip->dc_target_current_ma);
if (prev_therm_lvl == chip->thermal_levels - 1) {
/*
* If previously highest value was selected charging must have
* been disabed. Enable charging by taking the DC and USB path
* out of suspend.
*/
rc = smbchg_dc_en(chip, true, REASON_THERMAL);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set dc suspend rc %d\n", rc);
goto out;
}
rc = smbchg_usb_en(chip, true, REASON_THERMAL);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set usb suspend rc %d\n", rc);
goto out;
}
}
out:
mutex_unlock(&chip->current_change_lock);
return rc;
}
static int smbchg_ibat_ocp_threshold_ua = 4500000;
module_param(smbchg_ibat_ocp_threshold_ua, int, 0644);
#define UCONV 1000000LL
#define MCONV 1000LL
#define FLASH_V_THRESHOLD 3000000
#define FLASH_VDIP_MARGIN 100000
#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN)
#define BUCK_EFFICIENCY 800LL
static int smbchg_calc_max_flash_current(struct smbchg_chip *chip)
{
int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc;
int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv;
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
if (rc) {
pr_smb(PR_STATUS, "bms psy does not support OCV\n");
return 0;
}
rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE,
&esr_uohm);
if (rc) {
pr_smb(PR_STATUS, "bms psy does not support resistance\n");
return 0;
}
rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now);
if (rc) {
pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc);
return 0;
}
rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm;
/*
* Calculate the maximum current that can pulled out of the battery
* before the battery voltage dips below a safe threshold.
*/
ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV,
rbatt_uohm);
if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) {
/*
* If the calculated current is below the OCP threshold, then
* use it as the possible flash current.
*/
ibat_flash_ua = ibat_safe_ua - ibat_now;
vph_flash_uv = VPH_FLASH_VDIP;
} else {
/*
* If the calculated current is above the OCP threshold, then
* use the ocp threshold instead.
*
* Any higher current will be tripping the battery OCP.
*/
ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now;
vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
* smbchg_ibat_ocp_threshold_ua, UCONV);
}
/* Calculate the input voltage of the flash module. */
vin_flash_uv = max((chip->vled_max_uv + 500000LL),
div64_s64((vph_flash_uv * 1200), 1000));
/* Calculate the available power for the flash module. */
avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
/*
* Calculate the available amount of current the flash module can draw
* before collapsing the battery. (available power/ flash input voltage)
*/
avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
pr_smb(PR_MISC,
"avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
return (int)avail_flash_ua;
}
#define FCC_CMP_CFG 0xF3
#define FCC_COMP_MASK SMB_MASK(1, 0)
static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip,
int comp_current)
{
int rc;
u8 i;
for (i = 0; i < ARRAY_SIZE(fcc_comp_table); i++)
if (comp_current == fcc_comp_table[i])
break;
if (i >= ARRAY_SIZE(fcc_comp_table))
return -EINVAL;
rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG,
FCC_COMP_MASK, i);
if (rc)
dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
rc);
return rc;
}
#define FV_CMP_CFG 0xF5
#define FV_COMP_MASK SMB_MASK(5, 0)
static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code)
{
int rc;
u8 val;
val = code & FV_COMP_MASK;
rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG,
FV_COMP_MASK, val);
if (rc)
dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
rc);
return rc;
}
#define VFLOAT_CFG_REG 0xF4
#define MIN_FLOAT_MV 3600
#define MAX_FLOAT_MV 4500
#define VFLOAT_MASK SMB_MASK(5, 0)
#define MID_RANGE_FLOAT_MV_MIN 3600
#define MID_RANGE_FLOAT_MIN_VAL 0x05
#define MID_RANGE_FLOAT_STEP_MV 20
#define HIGH_RANGE_FLOAT_MIN_MV 4340
#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A
#define HIGH_RANGE_FLOAT_STEP_MV 10
#define VHIGH_RANGE_FLOAT_MIN_MV 4360
#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C
#define VHIGH_RANGE_FLOAT_STEP_MV 20
static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
int rc, delta;
u8 temp;
int float_voltage_comp;
if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
vfloat_mv);
return -EINVAL;
}
if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) {
/* mid range */
delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN;
temp = MID_RANGE_FLOAT_MIN_VAL + delta
/ MID_RANGE_FLOAT_STEP_MV;
vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV;
} else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) {
/* high range */
delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV;
temp = HIGH_RANGE_FLOAT_MIN_VAL + delta
/ HIGH_RANGE_FLOAT_STEP_MV;
vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV;
} else {
/* very high range */
delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV;
temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta
/ VHIGH_RANGE_FLOAT_STEP_MV;
vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV;
}
if (parallel_psy) {
rc = power_supply_set_voltage_limit(parallel_psy,
vfloat_mv + 50);
if (rc)
dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
rc);
}
rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG,
VFLOAT_MASK, temp);
if (rc)
dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc);
else
chip->vfloat_mv = vfloat_mv;
/* as stage charge vfloat have two value: 4.2V and 4.4V,
* stage charge will limit vfloat to 4.2V for first stage,
* then reset to 4.4V in second stage. when battery is warm,
* we should compensate the vfloat to protect the battery,
* and warm vfloat is 4.1V (according to battery datasheet),
* so if vfloat is 4.4V, use chip->float_voltage_comp = 16,
* if vfloat is 4.2V, use chip->float_voltage_comp_stage = 6.
*/
if (chip->vfloat_mv == chip->cfg_vfloat_mv) {
float_voltage_comp = chip->float_voltage_comp;
} else {
float_voltage_comp = chip->float_voltage_comp_stage;
}
/* set the float voltage compensation */
if (float_voltage_comp != -EINVAL) {
rc = smbchg_float_voltage_comp_set(chip,
float_voltage_comp);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
rc);
return rc;
}
pr_smb(PR_STATUS, "set float voltage comp to %d\n",
float_voltage_comp);
}
return rc;
}
static int smbchg_float_voltage_get(struct smbchg_chip *chip)
{
return chip->vfloat_mv;
}
#define SFT_CFG 0xFD
#define SFT_EN_MASK SMB_MASK(5, 4)
#define SFT_TO_MASK SMB_MASK(3, 2)
#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0)
#define SFT_TIMER_DISABLE_BIT BIT(5)
#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4)
#define SAFETY_TIME_MINUTES_SHIFT 2
static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable)
{
int rc;
u8 reg;
if (enable == chip->safety_timer_en)
return 0;
if (enable)
reg = 0;
else
reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT;
rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG,
SFT_EN_MASK, reg);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't %s safety timer rc = %d\n",
enable ? "enable" : "disable", rc);
return rc;
}
chip->safety_timer_en = enable;
return 0;
}
enum skip_reason {
REASON_OTG_ENABLED = BIT(0),
REASON_FLASH_ENABLED = BIT(1)
};
#define OTG_TRIM6 0xF6
#define TR_ENB_SKIP_BIT BIT(2)
#define OTG_EN_BIT BIT(0)
static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip,
enum skip_reason reason, bool disable)
{
int rc;
bool disabled;
disabled = !!chip->otg_pulse_skip_dis;
pr_smb(PR_STATUS, "%s pulse skip, reason %d\n",
disable ? "disabling" : "enabling", reason);
if (disable)
chip->otg_pulse_skip_dis |= reason;
else
chip->otg_pulse_skip_dis &= ~reason;
if (disabled == !!chip->otg_pulse_skip_dis)
return 0;
disabled = !!chip->otg_pulse_skip_dis;
rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6,
TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't %s otg pulse skip rc = %d\n",
disabled ? "disable" : "enable", rc);
return rc;
}
pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled");
return 0;
}
#define LOW_PWR_OPTIONS_REG 0xFF
#define FORCE_TLIM_BIT BIT(4)
static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable)
{
int rc;
rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG,
FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't %s otg force tlim rc = %d\n",
enable ? "enable" : "disable", rc);
return rc;
}
return rc;
}
static int smbchg_battery_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
int rc = 0;
bool unused;
struct smbchg_chip *chip = container_of(psy,
struct smbchg_chip, batt_psy);
switch (prop) {
case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
smbchg_battchg_en(chip, val->intval,
REASON_BATTCHG_USER, &unused);
break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
smbchg_usb_en(chip, val->intval, REASON_USER);
smbchg_dc_en(chip, val->intval, REASON_USER);
chip->chg_enabled = val->intval;
schedule_work(&chip->usb_set_online_work);
break;
case POWER_SUPPLY_PROP_CAPACITY:
chip->fake_battery_soc = val->intval;
power_supply_changed(&chip->batt_psy);
break;
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
smbchg_system_temp_level_set(chip, val->intval);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smbchg_float_voltage_set(chip, val->intval);
break;
case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
rc = smbchg_safety_timer_enable(chip, val->intval);
break;
case POWER_SUPPLY_PROP_FLASH_ACTIVE:
rc = smbchg_otg_pulse_skip_disable(chip,
REASON_FLASH_ENABLED, val->intval);
break;
case POWER_SUPPLY_PROP_FORCE_TLIM:
rc = smbchg_force_tlim_en(chip, val->intval);
break;
case POWER_SUPPLY_PROP_COMPASS_COMPENSATION:
update_compass_compensation(&chip->batt_psy);
break;
default:
return -EINVAL;
}
return rc;
}
static int smbchg_battery_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
int rc;
switch (prop) {
case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
case POWER_SUPPLY_PROP_CAPACITY:
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
rc = 1;
break;
default:
rc = 0;
break;
}
return rc;
}
static int smbchg_battery_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct smbchg_chip *chip = container_of(psy,
struct smbchg_chip, batt_psy);
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = get_prop_batt_status(chip);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = get_prop_batt_present(chip);