Created
February 10, 2017 12:51
-
-
Save bartcubbins/2aeb31ddd6ca45daed2009294a80390d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Copyright (c) 2013-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) "SMB:%s: " fmt, __func__ | |
| #include <linux/i2c.h> | |
| #include <linux/debugfs.h> | |
| #include <linux/gpio.h> | |
| #include <linux/errno.h> | |
| #include <linux/delay.h> | |
| #include <linux/module.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/math64.h> | |
| #include <linux/slab.h> | |
| #include <linux/power_supply.h> | |
| #include <linux/regulator/driver.h> | |
| #include <linux/regulator/of_regulator.h> | |
| #include <linux/regulator/machine.h> | |
| #include <linux/of.h> | |
| #include <linux/of_gpio.h> | |
| #include <linux/bitops.h> | |
| #include <linux/qpnp/qpnp-adc.h> | |
| #include <linux/completion.h> | |
| #include <linux/pm_wakeup.h> | |
| #define _SMB1360_MASK(BITS, POS) \ | |
| ((unsigned char)(((1 << (BITS)) - 1) << (POS))) | |
| #define SMB1360_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \ | |
| _SMB1360_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \ | |
| (RIGHT_BIT_POS)) | |
| /* Charger Registers */ | |
| #define CFG_BATT_CHG_REG 0x00 | |
| #define CHG_ITERM_MASK SMB1360_MASK(2, 0) | |
| #define CHG_ITERM_25MA 0x0 | |
| #define CHG_ITERM_200MA 0x7 | |
| #define RECHG_MV_MASK SMB1360_MASK(6, 5) | |
| #define RECHG_MV_SHIFT 5 | |
| #define OTG_CURRENT_MASK SMB1360_MASK(4, 3) | |
| #define OTG_CURRENT_SHIFT 3 | |
| #define CFG_BATT_CHG_ICL_REG 0x05 | |
| #define AC_INPUT_ICL_PIN_BIT BIT(7) | |
| #define AC_INPUT_PIN_HIGH_BIT BIT(6) | |
| #define RESET_STATE_USB_500 BIT(5) | |
| #define INPUT_CURR_LIM_MASK SMB1360_MASK(3, 0) | |
| #define INPUT_CURR_LIM_300MA 0x0 | |
| #define CFG_GLITCH_FLT_REG 0x06 | |
| #define AICL_ENABLED_BIT BIT(0) | |
| #define INPUT_UV_GLITCH_FLT_20MS_BIT BIT(7) | |
| #define CFG_CHG_MISC_REG 0x7 | |
| #define CHG_EN_BY_PIN_BIT BIT(7) | |
| #define CHG_EN_ACTIVE_LOW_BIT BIT(6) | |
| #define PRE_TO_FAST_REQ_CMD_BIT BIT(5) | |
| #define CFG_BAT_OV_ENDS_CHG_CYC BIT(4) | |
| #define CHG_CURR_TERM_DIS_BIT BIT(3) | |
| #define CFG_AUTO_RECHG_DIS_BIT BIT(2) | |
| #define CFG_CHG_INHIBIT_EN_BIT BIT(0) | |
| #define CFG_CHG_FUNC_CTRL_REG 0x08 | |
| #define CHG_RECHG_THRESH_FG_SRC_BIT BIT(1) | |
| #define CFG_STAT_CTRL_REG 0x09 | |
| #define CHG_STAT_IRQ_ONLY_BIT BIT(4) | |
| #define CHG_TEMP_CHG_ERR_BLINK_BIT BIT(3) | |
| #define CHG_STAT_ACTIVE_HIGH_BIT BIT(1) | |
| #define CHG_STAT_DISABLE_BIT BIT(0) | |
| #define CFG_SFY_TIMER_CTRL_REG 0x0A | |
| #define SAFETY_TIME_DISABLE_BIT BIT(5) | |
| #define SAFETY_TIME_MINUTES_SHIFT 2 | |
| #define SAFETY_TIME_MINUTES_MASK SMB1360_MASK(3, 2) | |
| #define CFG_BATT_MISSING_REG 0x0D | |
| #define BATT_MISSING_SRC_THERM_BIT BIT(1) | |
| #define CFG_FG_BATT_CTRL_REG 0x0E | |
| #define CFG_FG_OTP_BACK_UP_ENABLE BIT(7) | |
| #define BATT_ID_ENABLED_BIT BIT(5) | |
| #define CHG_BATT_ID_FAIL BIT(4) | |
| #define BATT_ID_FAIL_SELECT_PROFILE BIT(3) | |
| #define BATT_PROFILE_SELECT_MASK SMB1360_MASK(3, 0) | |
| #define BATT_PROFILEA_MASK 0x0 | |
| #define BATT_PROFILEB_MASK 0xF | |
| #define IRQ_CFG_REG 0x0F | |
| #define IRQ_BAT_HOT_COLD_HARD_BIT BIT(7) | |
| #define IRQ_BAT_HOT_COLD_SOFT_BIT BIT(6) | |
| #define IRQ_DCIN_UV_BIT BIT(2) | |
| #define IRQ_AICL_DONE_BIT BIT(1) | |
| #define IRQ_INTERNAL_TEMPERATURE_BIT BIT(0) | |
| #define IRQ2_CFG_REG 0x10 | |
| #define IRQ2_SAFETY_TIMER_BIT BIT(7) | |
| #define IRQ2_CHG_ERR_BIT BIT(6) | |
| #define IRQ2_CHG_PHASE_CHANGE_BIT BIT(4) | |
| #define IRQ2_POWER_OK_BIT BIT(2) | |
| #define IRQ2_BATT_MISSING_BIT BIT(1) | |
| #define IRQ2_VBAT_LOW_BIT BIT(0) | |
| #define IRQ3_CFG_REG 0x11 | |
| #define IRQ3_FG_ACCESS_OK_BIT BIT(6) | |
| #define IRQ3_SOC_CHANGE_BIT BIT(4) | |
| #define IRQ3_SOC_MIN_BIT BIT(3) | |
| #define IRQ3_SOC_MAX_BIT BIT(2) | |
| #define IRQ3_SOC_EMPTY_BIT BIT(1) | |
| #define IRQ3_SOC_FULL_BIT BIT(0) | |
| #define CHG_CURRENT_REG 0x13 | |
| #define FASTCHG_CURR_MASK SMB1360_MASK(4, 2) | |
| #define FASTCHG_CURR_SHIFT 2 | |
| #define CHG_CMP_CFG 0x14 | |
| #define JEITA_COMP_CURR_MASK SMB1360_MASK(3, 0) | |
| #define JEITA_COMP_EN_MASK SMB1360_MASK(7, 4) | |
| #define JEITA_COMP_EN_SHIFT 4 | |
| #define JEITA_COMP_EN_BIT SMB1360_MASK(7, 4) | |
| #define BATT_CHG_FLT_VTG_REG 0x15 | |
| #define VFLOAT_MASK SMB1360_MASK(6, 0) | |
| #define CFG_FVC_REG 0x16 | |
| #define FLT_VTG_COMP_MASK SMB1360_MASK(6, 0) | |
| #define SHDN_CTRL_REG 0x1A | |
| #define SHDN_CMD_USE_BIT BIT(1) | |
| #define SHDN_CMD_POLARITY_BIT BIT(2) | |
| #define CURRENT_GAIN_LSB_REG 0x1D | |
| #define CURRENT_GAIN_MSB_REG 0x1E | |
| /* Command Registers */ | |
| #define CMD_I2C_REG 0x40 | |
| #define ALLOW_VOLATILE_BIT BIT(6) | |
| #define FG_ACCESS_ENABLED_BIT BIT(5) | |
| #define FG_RESET_BIT BIT(4) | |
| #define CYCLE_STRETCH_CLEAR_BIT BIT(3) | |
| #define CMD_IL_REG 0x41 | |
| #define USB_CTRL_MASK SMB1360_MASK(1 , 0) | |
| #define USB_100_BIT 0x01 | |
| #define USB_500_BIT 0x00 | |
| #define USB_AC_BIT 0x02 | |
| #define SHDN_CMD_BIT BIT(7) | |
| #define CMD_CHG_REG 0x42 | |
| #define CMD_CHG_EN BIT(1) | |
| #define CMD_OTG_EN_BIT BIT(0) | |
| /* Status Registers */ | |
| #define STATUS_1_REG 0x48 | |
| #define AICL_CURRENT_STATUS_MASK SMB1360_MASK(6, 0) | |
| #define AICL_LIMIT_1500MA 0xF | |
| #define STATUS_3_REG 0x4B | |
| #define CHG_HOLD_OFF_BIT BIT(3) | |
| #define CHG_TYPE_MASK SMB1360_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 STATUS_4_REG 0x4C | |
| #define CYCLE_STRETCH_ACTIVE_BIT BIT(5) | |
| #define REVISION_CTRL_REG 0x4F | |
| #define DEVICE_REV_MASK SMB1360_MASK(3, 0) | |
| /* IRQ Status Registers */ | |
| #define IRQ_A_REG 0x50 | |
| #define IRQ_A_HOT_HARD_BIT BIT(6) | |
| #define IRQ_A_COLD_HARD_BIT BIT(4) | |
| #define IRQ_A_HOT_SOFT_BIT BIT(2) | |
| #define IRQ_A_COLD_SOFT_BIT BIT(0) | |
| #define IRQ_B_REG 0x51 | |
| #define IRQ_B_BATT_TERMINAL_BIT BIT(6) | |
| #define IRQ_B_BATT_MISSING_BIT BIT(4) | |
| #define IRQ_C_REG 0x52 | |
| #define IRQ_C_CHG_TERM BIT(0) | |
| #define IRQ_D_REG 0x53 | |
| #define IRQ_E_REG 0x54 | |
| #define IRQ_E_USBIN_UV_BIT BIT(0) | |
| #define IRQ_F_REG 0x55 | |
| #define IRQ_G_REG 0x56 | |
| #define IRQ_H_REG 0x57 | |
| #define IRQ_I_REG 0x58 | |
| #define FG_ACCESS_ALLOWED_BIT BIT(0) | |
| #define BATT_ID_RESULT_BIT SMB1360_MASK(6, 4) | |
| #define BATT_ID_SHIFT 4 | |
| /* FG registers - IRQ config register */ | |
| #define SOC_MAX_REG 0x24 | |
| #define SOC_MIN_REG 0x25 | |
| #define VTG_EMPTY_REG 0x26 | |
| #define SOC_DELTA_REG 0x28 | |
| #define JEITA_SOFT_COLD_REG 0x29 | |
| #define JEITA_SOFT_HOT_REG 0x2A | |
| #define VTG_MIN_REG 0x2B | |
| /* FG SHADOW registers */ | |
| #define SHDW_FG_ESR_ACTUAL 0x20 | |
| #define SHDW_FG_BATT_STATUS 0x60 | |
| #define BATTERY_PROFILE_BIT BIT(0) | |
| #define SHDW_FG_MSYS_SOC 0x61 | |
| #define SHDW_FG_CAPACITY 0x62 | |
| #define SHDW_FG_VTG_NOW 0x69 | |
| #define SHDW_FG_CURR_NOW 0x6B | |
| #define SHDW_FG_BATT_TEMP 0x6D | |
| #define VOLTAGE_PREDICTED_REG 0x80 | |
| #define CC_TO_SOC_COEFF 0xBA | |
| #define NOMINAL_CAPACITY_REG 0xBC | |
| #define ACTUAL_CAPACITY_REG 0xBE | |
| #define FG_AUTO_RECHARGE_SOC 0xD2 | |
| #define FG_SYS_CUTOFF_V_REG 0xD3 | |
| #define FG_CC_TO_CV_V_REG 0xD5 | |
| #define FG_ITERM_REG 0xD9 | |
| #define FG_THERM_C1_COEFF_REG 0xDB | |
| #define FG_IBATT_STANDBY_REG 0xCF | |
| #define FG_I2C_CFG_MASK SMB1360_MASK(2, 1) | |
| #define FG_CFG_I2C_ADDR 0x2 | |
| #define FG_PROFILE_A_ADDR 0x4 | |
| #define FG_PROFILE_B_ADDR 0x6 | |
| /* Constants */ | |
| #define CURRENT_100_MA 100 | |
| #define CURRENT_500_MA 500 | |
| #define MAX_8_BITS 255 | |
| #define JEITA_WORK_MS 3000 | |
| #define FG_RESET_THRESHOLD_MV 15 | |
| #define SMB1360_REV_1 0x01 | |
| #define SMB1360_POWERON_DELAY_MS 2000 | |
| #define SMB1360_FG_RESET_DELAY_MS 1500 | |
| enum { | |
| WRKRND_FG_CONFIG_FAIL = BIT(0), | |
| WRKRND_BATT_DET_FAIL = BIT(1), | |
| WRKRND_USB100_FAIL = BIT(2), | |
| WRKRND_HARD_JEITA = BIT(3), | |
| }; | |
| enum { | |
| USER = BIT(0), | |
| }; | |
| enum { | |
| PARALLEL_USER = BIT(0), | |
| PARALLEL_CURRENT = BIT(1), | |
| PARALLEL_JEITA_SOFT = BIT(2), | |
| PARALLEL_JEITA_HARD = BIT(3), | |
| PARALLEL_EOC = BIT(4), | |
| }; | |
| enum fg_i2c_access_type { | |
| FG_ACCESS_CFG = 0x1, | |
| FG_ACCESS_PROFILE_A = 0x2, | |
| FG_ACCESS_PROFILE_B = 0x3 | |
| }; | |
| enum { | |
| BATTERY_PROFILE_A, | |
| BATTERY_PROFILE_B, | |
| BATTERY_PROFILE_MAX, | |
| }; | |
| static int otg_curr_ma[] = {350, 550, 950, 1500}; | |
| struct otp_backup_pool { | |
| u8 reg_start; | |
| u8 reg_end; | |
| u8 start_now; | |
| u16 alg_bitmap; | |
| bool initialized; | |
| struct mutex lock; | |
| }; | |
| enum otp_backup_alg { | |
| OTP_BACKUP_NOT_USE = 0, | |
| OTP_BACKUP_FG_USE, | |
| OTP_BACKUP_PROF_A_USE, | |
| OTP_BACKUP_PROF_B_USE, | |
| }; | |
| struct smb1360_otg_regulator { | |
| struct regulator_desc rdesc; | |
| struct regulator_dev *rdev; | |
| }; | |
| enum wakeup_src { | |
| WAKEUP_SRC_FG_ACCESS = 0, | |
| WAKEUP_SRC_JEITA_SOFT, | |
| WAKEUP_SRC_PARALLEL, | |
| WAKEUP_SRC_MIN_SOC, | |
| WAKEUP_SRC_EMPTY_SOC, | |
| WAKEUP_SRC_JEITA_HYSTERSIS, | |
| WAKEUP_SRC_MAX, | |
| }; | |
| #define WAKEUP_SRC_MASK (~(~0 << WAKEUP_SRC_MAX)) | |
| struct smb1360_wakeup_source { | |
| struct wakeup_source source; | |
| unsigned long enabled_bitmap; | |
| spinlock_t ws_lock; | |
| }; | |
| struct smb1360_chip { | |
| struct i2c_client *client; | |
| struct device *dev; | |
| u8 revision; | |
| u8 soft_hot_rt_stat; | |
| u8 soft_cold_rt_stat; | |
| struct delayed_work jeita_work; | |
| struct delayed_work delayed_init_work; | |
| unsigned short default_i2c_addr; | |
| unsigned short fg_i2c_addr; | |
| bool pulsed_irq; | |
| struct completion fg_mem_access_granted; | |
| /* wakeup source */ | |
| struct smb1360_wakeup_source smb1360_ws; | |
| /* configuration data - charger */ | |
| int fake_battery_soc; | |
| bool batt_id_disabled; | |
| bool charging_disabled; | |
| bool recharge_disabled; | |
| bool chg_inhibit_disabled; | |
| bool iterm_disabled; | |
| bool shdn_after_pwroff; | |
| bool config_hard_thresholds; | |
| bool soft_jeita_supported; | |
| bool ov_ends_chg_cycle_disabled; | |
| int iterm_ma; | |
| int vfloat_mv; | |
| int safety_time; | |
| int resume_delta_mv; | |
| u32 default_batt_profile; | |
| unsigned int thermal_levels; | |
| unsigned int therm_lvl_sel; | |
| unsigned int *thermal_mitigation; | |
| int otg_batt_curr_limit; | |
| bool min_icl_usb100; | |
| int cold_bat_decidegc; | |
| int hot_bat_decidegc; | |
| int cool_bat_decidegc; | |
| int warm_bat_decidegc; | |
| int cool_bat_mv; | |
| int warm_bat_mv; | |
| int cool_bat_ma; | |
| int warm_bat_ma; | |
| int soft_cold_thresh; | |
| int soft_hot_thresh; | |
| /* parallel-chg params */ | |
| int fastchg_current; | |
| int parallel_chg_disable_status; | |
| int max_parallel_chg_current; | |
| bool parallel_charging; | |
| /* configuration data - fg */ | |
| int soc_max; | |
| int soc_min; | |
| int delta_soc; | |
| int voltage_min_mv; | |
| int voltage_empty_mv; | |
| int batt_capacity_mah; | |
| int cc_soc_coeff; | |
| int v_cutoff_mv; | |
| int fg_iterm_ma; | |
| int fg_ibatt_standby_ma; | |
| int fg_thermistor_c1_coeff; | |
| int fg_cc_to_cv_mv; | |
| int fg_auto_recharge_soc; | |
| bool empty_soc_disabled; | |
| int fg_reset_threshold_mv; | |
| bool fg_reset_at_pon; | |
| bool rsense_10mohm; | |
| bool otg_fet_present; | |
| bool fet_gain_enabled; | |
| int otg_fet_enable_gpio; | |
| /* status tracking */ | |
| int voltage_now; | |
| int current_now; | |
| int resistance_now; | |
| int temp_now; | |
| int soc_now; | |
| int fcc_mah; | |
| bool usb_present; | |
| bool batt_present; | |
| bool batt_hot; | |
| bool batt_cold; | |
| bool batt_warm; | |
| bool batt_cool; | |
| bool batt_full; | |
| bool resume_completed; | |
| bool irq_waiting; | |
| bool irq_disabled; | |
| bool empty_soc; | |
| bool awake_min_soc; | |
| int workaround_flags; | |
| u8 irq_cfg_mask[3]; | |
| int usb_psy_ma; | |
| int charging_disabled_status; | |
| u32 connected_rid; | |
| u32 profile_rid[BATTERY_PROFILE_MAX]; | |
| u32 peek_poke_address; | |
| u32 fg_access_type; | |
| u32 fg_peek_poke_address; | |
| int skip_writes; | |
| int skip_reads; | |
| struct dentry *debug_root; | |
| struct qpnp_vadc_chip *vadc_dev; | |
| struct power_supply *parallel_psy; | |
| struct power_supply *usb_psy; | |
| struct power_supply batt_psy; | |
| struct smb1360_otg_regulator otg_vreg; | |
| struct mutex irq_complete; | |
| struct mutex charging_disable_lock; | |
| struct mutex current_change_lock; | |
| struct mutex read_write_lock; | |
| struct mutex parallel_chg_lock; | |
| struct work_struct parallel_work; | |
| struct mutex otp_gain_lock; | |
| struct mutex fg_access_request_lock; | |
| struct otp_backup_pool otp_backup; | |
| u8 current_gain_otp_reg; | |
| bool otp_hard_jeita_config; | |
| int otp_cold_bat_decidegc; | |
| int otp_hot_bat_decidegc; | |
| u8 hard_jeita_otp_reg; | |
| struct work_struct jeita_hysteresis_work; | |
| int cold_hysteresis; | |
| int hot_hysteresis; | |
| }; | |
| static int chg_time[] = { | |
| 192, | |
| 384, | |
| 768, | |
| 1536, | |
| }; | |
| static int input_current_limit[] = { | |
| 300, 400, 450, 500, 600, 700, 800, 850, 900, | |
| 950, 1000, 1100, 1200, 1300, 1400, 1500, | |
| }; | |
| static int fastchg_current[] = { | |
| 450, 600, 750, 900, 1050, 1200, 1350, 1500, | |
| }; | |
| static void smb1360_stay_awake(struct smb1360_wakeup_source *source, | |
| enum wakeup_src wk_src) | |
| { | |
| unsigned long flags; | |
| spin_lock_irqsave(&source->ws_lock, flags); | |
| if (!__test_and_set_bit(wk_src, &source->enabled_bitmap)) { | |
| __pm_stay_awake(&source->source); | |
| pr_debug("enabled source %s, wakeup_src %d\n", | |
| source->source.name, wk_src); | |
| } | |
| spin_unlock_irqrestore(&source->ws_lock, flags); | |
| } | |
| static void smb1360_relax(struct smb1360_wakeup_source *source, | |
| enum wakeup_src wk_src) | |
| { | |
| unsigned long flags; | |
| spin_lock_irqsave(&source->ws_lock, flags); | |
| if (__test_and_clear_bit(wk_src, &source->enabled_bitmap) && | |
| !(source->enabled_bitmap & WAKEUP_SRC_MASK)) { | |
| __pm_relax(&source->source); | |
| pr_debug("disabled source %s\n", source->source.name); | |
| } | |
| spin_unlock_irqrestore(&source->ws_lock, flags); | |
| pr_debug("relax source %s, wakeup_src %d\n", | |
| source->source.name, wk_src); | |
| } | |
| static void smb1360_wakeup_src_init(struct smb1360_chip *chip) | |
| { | |
| spin_lock_init(&chip->smb1360_ws.ws_lock); | |
| wakeup_source_init(&chip->smb1360_ws.source, "smb1360"); | |
| } | |
| static int is_between(int value, int left, int right) | |
| { | |
| if (left >= right && left >= value && value >= right) | |
| return 1; | |
| if (left <= right && left <= value && value <= right) | |
| return 1; | |
| return 0; | |
| } | |
| static int bound(int val, int min, int max) | |
| { | |
| if (val < min) | |
| return min; | |
| if (val > max) | |
| return max; | |
| return val; | |
| } | |
| static int __smb1360_read(struct smb1360_chip *chip, int reg, | |
| u8 *val) | |
| { | |
| s32 ret; | |
| ret = i2c_smbus_read_byte_data(chip->client, reg); | |
| if (ret < 0) { | |
| dev_err(chip->dev, | |
| "i2c read fail: can't read from %02x: %d\n", reg, ret); | |
| return ret; | |
| } else { | |
| *val = ret; | |
| } | |
| pr_debug("Reading 0x%02x=0x%02x\n", reg, *val); | |
| return 0; | |
| } | |
| static int __smb1360_write(struct smb1360_chip *chip, int reg, | |
| u8 val) | |
| { | |
| s32 ret; | |
| ret = i2c_smbus_write_byte_data(chip->client, reg, val); | |
| if (ret < 0) { | |
| dev_err(chip->dev, | |
| "i2c write fail: can't write %02x to %02x: %d\n", | |
| val, reg, ret); | |
| return ret; | |
| } | |
| pr_debug("Writing 0x%02x=0x%02x\n", reg, val); | |
| return 0; | |
| } | |
| static int smb1360_read(struct smb1360_chip *chip, int reg, | |
| u8 *val) | |
| { | |
| int rc; | |
| if (chip->skip_reads) { | |
| *val = 0; | |
| return 0; | |
| } | |
| mutex_lock(&chip->read_write_lock); | |
| rc = __smb1360_read(chip, reg, val); | |
| mutex_unlock(&chip->read_write_lock); | |
| return rc; | |
| } | |
| static int smb1360_write(struct smb1360_chip *chip, int reg, | |
| u8 val) | |
| { | |
| int rc; | |
| if (chip->skip_writes) | |
| return 0; | |
| mutex_lock(&chip->read_write_lock); | |
| rc = __smb1360_write(chip, reg, val); | |
| mutex_unlock(&chip->read_write_lock); | |
| return rc; | |
| } | |
| static int smb1360_fg_read(struct smb1360_chip *chip, int reg, | |
| u8 *val) | |
| { | |
| int rc; | |
| if (chip->skip_reads) { | |
| *val = 0; | |
| return 0; | |
| } | |
| mutex_lock(&chip->read_write_lock); | |
| chip->client->addr = chip->fg_i2c_addr; | |
| rc = __smb1360_read(chip, reg, val); | |
| chip->client->addr = chip->default_i2c_addr; | |
| mutex_unlock(&chip->read_write_lock); | |
| return rc; | |
| } | |
| static int smb1360_fg_write(struct smb1360_chip *chip, int reg, | |
| u8 val) | |
| { | |
| int rc; | |
| if (chip->skip_writes) | |
| return 0; | |
| mutex_lock(&chip->read_write_lock); | |
| chip->client->addr = chip->fg_i2c_addr; | |
| rc = __smb1360_write(chip, reg, val); | |
| chip->client->addr = chip->default_i2c_addr; | |
| mutex_unlock(&chip->read_write_lock); | |
| return rc; | |
| } | |
| static int smb1360_read_bytes(struct smb1360_chip *chip, int reg, | |
| u8 *val, u8 bytes) | |
| { | |
| s32 rc; | |
| if (chip->skip_reads) { | |
| *val = 0; | |
| return 0; | |
| } | |
| mutex_lock(&chip->read_write_lock); | |
| rc = i2c_smbus_read_i2c_block_data(chip->client, reg, bytes, val); | |
| if (rc < 0) | |
| dev_err(chip->dev, | |
| "i2c read fail: can't read %d bytes from %02x: %d\n", | |
| bytes, reg, rc); | |
| mutex_unlock(&chip->read_write_lock); | |
| return (rc < 0) ? rc : 0; | |
| } | |
| static int smb1360_write_bytes(struct smb1360_chip *chip, int reg, | |
| u8 *val, u8 bytes) | |
| { | |
| s32 rc; | |
| if (chip->skip_writes) { | |
| *val = 0; | |
| return 0; | |
| } | |
| mutex_lock(&chip->read_write_lock); | |
| rc = i2c_smbus_write_i2c_block_data(chip->client, reg, bytes, val); | |
| if (rc < 0) | |
| dev_err(chip->dev, | |
| "i2c write fail: can't read %d bytes from %02x: %d\n", | |
| bytes, reg, rc); | |
| mutex_unlock(&chip->read_write_lock); | |
| return (rc < 0) ? rc : 0; | |
| } | |
| static int smb1360_masked_write(struct smb1360_chip *chip, int reg, | |
| u8 mask, u8 val) | |
| { | |
| s32 rc; | |
| u8 temp; | |
| if (chip->skip_writes || chip->skip_reads) | |
| return 0; | |
| mutex_lock(&chip->read_write_lock); | |
| rc = __smb1360_read(chip, reg, &temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "read failed: reg=%03X, rc=%d\n", reg, rc); | |
| goto out; | |
| } | |
| temp &= ~mask; | |
| temp |= val & mask; | |
| rc = __smb1360_write(chip, reg, temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "write failed: reg=%03X, rc=%d\n", reg, rc); | |
| } | |
| out: | |
| mutex_unlock(&chip->read_write_lock); | |
| return rc; | |
| } | |
| static int smb1360_select_fg_i2c_address(struct smb1360_chip *chip) | |
| { | |
| unsigned short addr = chip->default_i2c_addr << 0x1; | |
| switch (chip->fg_access_type) { | |
| case FG_ACCESS_CFG: | |
| addr = (addr & ~FG_I2C_CFG_MASK) | FG_CFG_I2C_ADDR; | |
| break; | |
| case FG_ACCESS_PROFILE_A: | |
| addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_A_ADDR; | |
| break; | |
| case FG_ACCESS_PROFILE_B: | |
| addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_B_ADDR; | |
| break; | |
| default: | |
| pr_err("Invalid FG access type=%d\n", chip->fg_access_type); | |
| return -EINVAL; | |
| } | |
| chip->fg_i2c_addr = addr >> 0x1; | |
| pr_debug("FG_access_type=%d fg_i2c_addr=%x\n", chip->fg_access_type, | |
| chip->fg_i2c_addr); | |
| return 0; | |
| } | |
| #define EXPONENT_MASK 0xF800 | |
| #define MANTISSA_MASK 0x3FF | |
| #define SIGN_MASK 0x400 | |
| #define EXPONENT_SHIFT 11 | |
| #define SIGN_SHIFT 10 | |
| #define MICRO_UNIT 1000000ULL | |
| static int64_t float_decode(u16 reg) | |
| { | |
| int64_t final_val, exponent_val, mantissa_val; | |
| int exponent, mantissa, n; | |
| bool sign; | |
| exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT; | |
| mantissa = (reg & MANTISSA_MASK); | |
| sign = !!(reg & SIGN_MASK); | |
| pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign); | |
| mantissa_val = mantissa * MICRO_UNIT; | |
| n = exponent - 15; | |
| if (n < 0) | |
| exponent_val = MICRO_UNIT >> -n; | |
| else | |
| exponent_val = MICRO_UNIT << n; | |
| n = n - 10; | |
| if (n < 0) | |
| mantissa_val >>= -n; | |
| else | |
| mantissa_val <<= n; | |
| final_val = exponent_val + mantissa_val; | |
| if (sign) | |
| final_val *= -1; | |
| return final_val; | |
| } | |
| #define MAX_MANTISSA (1023 * 1000000ULL) | |
| unsigned int float_encode(int64_t float_val) | |
| { | |
| int exponent = 0, sign = 0; | |
| unsigned int final_val = 0; | |
| if (float_val == 0) | |
| return 0; | |
| if (float_val < 0) { | |
| sign = 1; | |
| float_val = -float_val; | |
| } | |
| /* Reduce large mantissa until it fits into 10 bit */ | |
| while (float_val >= MAX_MANTISSA) { | |
| exponent++; | |
| float_val >>= 1; | |
| } | |
| /* Increase small mantissa to improve precision */ | |
| while (float_val < MAX_MANTISSA && exponent > -25) { | |
| exponent--; | |
| float_val <<= 1; | |
| } | |
| exponent = exponent + 25; | |
| /* Convert mantissa from micro-units to units */ | |
| float_val = div_s64((float_val + MICRO_UNIT), (int)MICRO_UNIT); | |
| if (float_val == 1024) { | |
| exponent--; | |
| float_val <<= 1; | |
| } | |
| float_val -= 1024; | |
| /* Ensure that resulting number is within range */ | |
| if (float_val > MANTISSA_MASK) | |
| float_val = MANTISSA_MASK; | |
| /* Convert to 5 bit exponent, 11 bit mantissa */ | |
| final_val = (float_val & MANTISSA_MASK) | (sign << SIGN_SHIFT) | | |
| ((exponent << EXPONENT_SHIFT) & EXPONENT_MASK); | |
| return final_val; | |
| } | |
| /* FG reset could only be done after FG access being granted */ | |
| static int smb1360_force_fg_reset(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT, | |
| FG_RESET_BIT); | |
| if (rc) { | |
| pr_err("Couldn't reset FG rc=%d\n", rc); | |
| return rc; | |
| } | |
| msleep(SMB1360_FG_RESET_DELAY_MS); | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT, 0); | |
| if (rc) | |
| pr_err("Couldn't un-reset FG rc=%d\n", rc); | |
| return rc; | |
| } | |
| /* | |
| * Requesting FG access relys on the FG_ACCESS_ALLOWED IRQ. | |
| * This function can only be called after interrupt handler | |
| * being installed successfully. | |
| */ | |
| #define SMB1360_FG_ACCESS_TIMEOUT_MS 5000 | |
| #define SMB1360_FG_ACCESS_RETRY_COUNT 3 | |
| static int smb1360_enable_fg_access(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| u8 reg, retry = SMB1360_FG_ACCESS_RETRY_COUNT; | |
| pr_debug("request FG memory access\n"); | |
| /* | |
| * read the ACCESS_ALLOW status bit firstly to | |
| * check if the access was granted before | |
| */ | |
| mutex_lock(&chip->fg_access_request_lock); | |
| smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS); | |
| rc = smb1360_read(chip, IRQ_I_REG, ®); | |
| if (rc) { | |
| pr_err("Couldn't read IRQ_I_REG, rc=%d\n", rc); | |
| goto bail_i2c; | |
| } else if (reg & FG_ACCESS_ALLOWED_BIT) { | |
| pr_debug("FG access was granted\n"); | |
| goto bail_i2c; | |
| } | |
| /* request FG access */ | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, | |
| FG_ACCESS_ENABLED_BIT); | |
| if (rc) { | |
| pr_err("Couldn't enable FG access rc=%d\n", rc); | |
| goto bail_i2c; | |
| } | |
| while (retry--) { | |
| rc = wait_for_completion_interruptible_timeout( | |
| &chip->fg_mem_access_granted, | |
| msecs_to_jiffies(SMB1360_FG_ACCESS_TIMEOUT_MS)); | |
| if (rc <= 0) | |
| pr_debug("FG access timeout, retry: %d\n", retry); | |
| else | |
| break; | |
| } | |
| if (rc == 0) /* timed out */ | |
| rc = -ETIMEDOUT; | |
| else if (rc > 0) /* completed */ | |
| rc = 0; | |
| /* Clear the FG access bit if request failed */ | |
| if (rc < 0) { | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, | |
| FG_ACCESS_ENABLED_BIT, 0); | |
| if (rc) | |
| pr_err("Couldn't disable FG access rc=%d\n", rc); | |
| } | |
| bail_i2c: | |
| smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS); | |
| mutex_unlock(&chip->fg_access_request_lock); | |
| return rc; | |
| } | |
| static inline bool is_device_suspended(struct smb1360_chip *chip) | |
| { | |
| return !chip->resume_completed; | |
| } | |
| static int smb1360_disable_fg_access(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0); | |
| if (rc) | |
| pr_err("Couldn't disable FG access rc=%d\n", rc); | |
| INIT_COMPLETION(chip->fg_mem_access_granted); | |
| return rc; | |
| } | |
| static int smb1360_enable_volatile_writes(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, | |
| ALLOW_VOLATILE_BIT, ALLOW_VOLATILE_BIT); | |
| if (rc < 0) | |
| dev_err(chip->dev, | |
| "Couldn't set VOLATILE_W_PERM_BIT rc=%d\n", rc); | |
| return rc; | |
| } | |
| void smb1360_otp_backup_pool_init(struct smb1360_chip *chip) | |
| { | |
| struct otp_backup_pool *pool = &chip->otp_backup; | |
| pool->reg_start = 0xE0; | |
| pool->reg_end = 0xEF; | |
| pool->start_now = pool->reg_start; | |
| mutex_init(&pool->lock); | |
| } | |
| static int smb1360_alloc_otp_backup_register(struct smb1360_chip *chip, | |
| u8 size, int usage) | |
| { | |
| int rc = 0, i; | |
| u8 inv_pos; | |
| struct otp_backup_pool *pool = &chip->otp_backup; | |
| if (size % 2) { | |
| pr_err("Must be allocated with pairs\n"); | |
| return -EINVAL; | |
| } | |
| mutex_lock(&pool->lock); | |
| if (pool->start_now + size > pool->reg_end) { | |
| pr_err("Allocation fail: start = 0x%x, size = %d\n", | |
| pool->start_now, size); | |
| mutex_unlock(&pool->lock); | |
| return -EBUSY; | |
| } | |
| rc = pool->start_now; | |
| inv_pos = pool->reg_end - pool->start_now + 1; | |
| for (i = 0; i < size; i = i + 2) { | |
| inv_pos -= i; | |
| pool->alg_bitmap |= usage << (inv_pos - 2); | |
| } | |
| pr_debug("Allocation success, start = 0x%x, size = %d, alg_bitmap = 0x%x\n", | |
| rc, size, pool->alg_bitmap); | |
| pool->start_now += size; | |
| mutex_unlock(&pool->lock); | |
| return rc; | |
| } | |
| #define OTP_BACKUP_WA_ALG_1 0xF0 | |
| #define OTP_BACKUP_WA_ALG_2 0xF1 | |
| static int smb1360_otp_backup_alg_update(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| struct otp_backup_pool *pool = &chip->otp_backup; | |
| mutex_lock(&pool->lock); | |
| rc = smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_1, | |
| (u8)(pool->alg_bitmap >> 8)); | |
| rc |= smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_2, | |
| (u8)(pool->alg_bitmap)); | |
| if (rc) | |
| pr_err("Write FG address F0/F1 failed, rc = %d\n", rc); | |
| mutex_unlock(&pool->lock); | |
| return rc; | |
| } | |
| #define TRIM_1C_REG 0x1C | |
| #define CHECK_USB100_GOOD_BIT BIT(6) | |
| static bool is_usb100_broken(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| u8 reg; | |
| rc = smb1360_read(chip, TRIM_1C_REG, ®); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read trim 1C reg rc = %d\n", rc); | |
| return rc; | |
| } | |
| return !!(reg & CHECK_USB100_GOOD_BIT); | |
| } | |
| static int read_revision(struct smb1360_chip *chip, u8 *revision) | |
| { | |
| int rc; | |
| *revision = 0; | |
| rc = smb1360_read(chip, REVISION_CTRL_REG, revision); | |
| if (rc) | |
| dev_err(chip->dev, "Couldn't read REVISION_CTRL_REG rc=%d", rc); | |
| *revision &= DEVICE_REV_MASK; | |
| return rc; | |
| } | |
| #define MIN_FLOAT_MV 3460 | |
| #define MAX_FLOAT_MV 4730 | |
| #define VFLOAT_STEP_MV 10 | |
| static int smb1360_float_voltage_set(struct smb1360_chip *chip, int vfloat_mv) | |
| { | |
| u8 temp; | |
| 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; | |
| } | |
| temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV; | |
| return smb1360_masked_write(chip, BATT_CHG_FLT_VTG_REG, | |
| VFLOAT_MASK, temp); | |
| } | |
| #define MIN_RECHG_MV 50 | |
| #define MAX_RECHG_MV 300 | |
| static int smb1360_recharge_threshold_set(struct smb1360_chip *chip, | |
| int resume_mv) | |
| { | |
| u8 temp; | |
| if ((resume_mv < MIN_RECHG_MV) || (resume_mv > MAX_RECHG_MV)) { | |
| dev_err(chip->dev, "bad rechg_thrsh =%d asked to set\n", | |
| resume_mv); | |
| return -EINVAL; | |
| } | |
| temp = resume_mv / 100; | |
| return smb1360_masked_write(chip, CFG_BATT_CHG_REG, | |
| RECHG_MV_MASK, temp << RECHG_MV_SHIFT); | |
| } | |
| static int __smb1360_charging_disable(struct smb1360_chip *chip, bool disable) | |
| { | |
| int rc; | |
| rc = smb1360_masked_write(chip, CMD_CHG_REG, | |
| CMD_CHG_EN, disable ? 0 : CMD_CHG_EN); | |
| if (rc < 0) | |
| pr_err("Couldn't set CHG_ENABLE_BIT disable=%d rc = %d\n", | |
| disable, rc); | |
| else | |
| pr_debug("CHG_EN status=%d\n", !disable); | |
| return rc; | |
| } | |
| static int smb1360_charging_disable(struct smb1360_chip *chip, int reason, | |
| int disable) | |
| { | |
| int rc = 0; | |
| int disabled; | |
| mutex_lock(&chip->charging_disable_lock); | |
| disabled = chip->charging_disabled_status; | |
| pr_debug("reason=%d requested_disable=%d disabled_status=%d\n", | |
| reason, disable, disabled); | |
| if (disable == true) | |
| disabled |= reason; | |
| else | |
| disabled &= ~reason; | |
| if (disabled) | |
| rc = __smb1360_charging_disable(chip, true); | |
| else | |
| rc = __smb1360_charging_disable(chip, false); | |
| if (rc) | |
| pr_err("Couldn't disable charging for reason=%d rc=%d\n", | |
| rc, reason); | |
| else | |
| chip->charging_disabled_status = disabled; | |
| mutex_unlock(&chip->charging_disable_lock); | |
| return rc; | |
| } | |
| static int smb1360_soft_jeita_comp_enable(struct smb1360_chip *chip, | |
| bool enable) | |
| { | |
| int rc = 0; | |
| rc = smb1360_masked_write(chip, CHG_CMP_CFG, JEITA_COMP_EN_MASK, | |
| enable ? JEITA_COMP_EN_BIT : 0); | |
| if (rc) | |
| pr_err("Couldn't %s JEITA compensation\n", enable ? | |
| "enable" : "disable"); | |
| return rc; | |
| } | |
| static enum power_supply_property smb1360_battery_properties[] = { | |
| POWER_SUPPLY_PROP_HEALTH, | |
| POWER_SUPPLY_PROP_STATUS, | |
| POWER_SUPPLY_PROP_PRESENT, | |
| POWER_SUPPLY_PROP_CHARGING_ENABLED, | |
| POWER_SUPPLY_PROP_CHARGE_TYPE, | |
| POWER_SUPPLY_PROP_CAPACITY, | |
| POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | |
| POWER_SUPPLY_PROP_VOLTAGE_NOW, | |
| POWER_SUPPLY_PROP_CURRENT_NOW, | |
| POWER_SUPPLY_PROP_RESISTANCE, | |
| POWER_SUPPLY_PROP_TEMP, | |
| POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, | |
| }; | |
| static int smb1360_get_prop_batt_present(struct smb1360_chip *chip) | |
| { | |
| return chip->batt_present; | |
| } | |
| static int smb1360_get_prop_batt_status(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| u8 reg = 0, chg_type; | |
| if (is_device_suspended(chip)) | |
| return POWER_SUPPLY_STATUS_UNKNOWN; | |
| if (chip->batt_full) | |
| return POWER_SUPPLY_STATUS_FULL; | |
| rc = smb1360_read(chip, STATUS_3_REG, ®); | |
| if (rc) { | |
| pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc); | |
| return POWER_SUPPLY_STATUS_UNKNOWN; | |
| } | |
| pr_debug("STATUS_3_REG = %x\n", reg); | |
| if (reg & CHG_HOLD_OFF_BIT) | |
| return POWER_SUPPLY_STATUS_NOT_CHARGING; | |
| chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; | |
| if (chg_type == BATT_NOT_CHG_VAL) | |
| return POWER_SUPPLY_STATUS_DISCHARGING; | |
| else | |
| return POWER_SUPPLY_STATUS_CHARGING; | |
| } | |
| static int smb1360_get_prop_charge_type(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| u8 reg = 0; | |
| u8 chg_type; | |
| if (is_device_suspended(chip)) | |
| return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; | |
| rc = smb1360_read(chip, STATUS_3_REG, ®); | |
| if (rc) { | |
| pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc); | |
| return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; | |
| } | |
| 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_FAST_CHG_VAL) || | |
| (chg_type == BATT_TAPER_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 smb1360_get_prop_batt_health(struct smb1360_chip *chip) | |
| { | |
| union power_supply_propval ret = {0, }; | |
| if (chip->batt_hot) | |
| ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT; | |
| else if (chip->batt_cold) | |
| ret.intval = POWER_SUPPLY_HEALTH_COLD; | |
| else if (chip->batt_warm) | |
| ret.intval = POWER_SUPPLY_HEALTH_WARM; | |
| else if (chip->batt_cool) | |
| ret.intval = POWER_SUPPLY_HEALTH_COOL; | |
| else | |
| ret.intval = POWER_SUPPLY_HEALTH_GOOD; | |
| return ret.intval; | |
| } | |
| static int smb1360_get_prop_batt_capacity(struct smb1360_chip *chip) | |
| { | |
| u8 reg; | |
| u32 temp = 0; | |
| int rc, soc = 0; | |
| if (chip->fake_battery_soc >= 0) | |
| return chip->fake_battery_soc; | |
| if (chip->empty_soc) { | |
| pr_debug("empty_soc\n"); | |
| return 0; | |
| } | |
| if (is_device_suspended(chip)) | |
| return chip->soc_now; | |
| rc = smb1360_read(chip, SHDW_FG_MSYS_SOC, ®); | |
| if (rc) { | |
| pr_err("Failed to read FG_MSYS_SOC rc=%d\n", rc); | |
| return rc; | |
| } | |
| soc = (100 * reg) / MAX_8_BITS; | |
| temp = (100 * reg) % MAX_8_BITS; | |
| if (temp > (MAX_8_BITS / 2)) | |
| soc += 1; | |
| pr_debug("msys_soc_reg=0x%02x, fg_soc=%d batt_full = %d\n", reg, | |
| soc, chip->batt_full); | |
| chip->soc_now = (chip->batt_full ? 100 : bound(soc, 0, 100)); | |
| return chip->soc_now; | |
| } | |
| static int smb1360_get_prop_chg_full_design(struct smb1360_chip *chip) | |
| { | |
| u8 reg[2]; | |
| int rc, fcc_mah = 0; | |
| if (is_device_suspended(chip)) | |
| return chip->fcc_mah; | |
| rc = smb1360_read_bytes(chip, SHDW_FG_CAPACITY, reg, 2); | |
| if (rc) { | |
| pr_err("Failed to read SHDW_FG_CAPACITY rc=%d\n", rc); | |
| return rc; | |
| } | |
| fcc_mah = (reg[1] << 8) | reg[0]; | |
| pr_debug("reg[0]=0x%02x reg[1]=0x%02x fcc_mah=%d\n", | |
| reg[0], reg[1], fcc_mah); | |
| chip->fcc_mah = fcc_mah * 1000; | |
| return chip->fcc_mah; | |
| } | |
| static int smb1360_get_prop_batt_temp(struct smb1360_chip *chip) | |
| { | |
| u8 reg[2]; | |
| int rc, temp = 0; | |
| if (is_device_suspended(chip)) | |
| return chip->temp_now; | |
| rc = smb1360_read_bytes(chip, SHDW_FG_BATT_TEMP, reg, 2); | |
| if (rc) { | |
| pr_err("Failed to read SHDW_FG_BATT_TEMP rc=%d\n", rc); | |
| return rc; | |
| } | |
| temp = (reg[1] << 8) | reg[0]; | |
| temp = div_u64(temp * 625, 10000UL); /* temperature in kelvin */ | |
| temp = (temp - 273) * 10; /* temperature in decideg */ | |
| pr_debug("reg[0]=0x%02x reg[1]=0x%02x temperature=%d\n", | |
| reg[0], reg[1], temp); | |
| chip->temp_now = temp; | |
| return chip->temp_now; | |
| } | |
| static int smb1360_get_prop_voltage_now(struct smb1360_chip *chip) | |
| { | |
| u8 reg[2]; | |
| int rc, temp = 0; | |
| if (is_device_suspended(chip)) | |
| return chip->voltage_now; | |
| rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg, 2); | |
| if (rc) { | |
| pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc); | |
| return rc; | |
| } | |
| temp = (reg[1] << 8) | reg[0]; | |
| temp = div_u64(temp * 5000, 0x7FFF); | |
| pr_debug("reg[0]=0x%02x reg[1]=0x%02x voltage=%d\n", | |
| reg[0], reg[1], temp * 1000); | |
| chip->voltage_now = temp * 1000; | |
| return chip->voltage_now; | |
| } | |
| static int smb1360_get_prop_batt_resistance(struct smb1360_chip *chip) | |
| { | |
| u8 reg[2]; | |
| u16 temp; | |
| int rc; | |
| int64_t resistance; | |
| if (is_device_suspended(chip)) | |
| return chip->resistance_now; | |
| rc = smb1360_read_bytes(chip, SHDW_FG_ESR_ACTUAL, reg, 2); | |
| if (rc) { | |
| pr_err("Failed to read FG_ESR_ACTUAL rc=%d\n", rc); | |
| return rc; | |
| } | |
| temp = (reg[1] << 8) | reg[0]; | |
| resistance = float_decode(temp) * 2; | |
| pr_debug("reg=0x%02x resistance=%lld\n", temp, resistance); | |
| /* resistance in uohms */ | |
| chip->resistance_now = resistance; | |
| return chip->resistance_now; | |
| } | |
| static int smb1360_get_prop_current_now(struct smb1360_chip *chip) | |
| { | |
| u8 reg[2]; | |
| int rc, temp = 0; | |
| if (is_device_suspended(chip)) | |
| return chip->current_now; | |
| rc = smb1360_read_bytes(chip, SHDW_FG_CURR_NOW, reg, 2); | |
| if (rc) { | |
| pr_err("Failed to read SHDW_FG_CURR_NOW rc=%d\n", rc); | |
| return rc; | |
| } | |
| temp = ((s8)reg[1] << 8) | reg[0]; | |
| temp = div_s64(temp * 2500, 0x7FFF); | |
| pr_debug("reg[0]=0x%02x reg[1]=0x%02x current=%d\n", | |
| reg[0], reg[1], temp * 1000); | |
| chip->current_now = temp * 1000; | |
| return chip->current_now; | |
| } | |
| static int smb1360_set_minimum_usb_current(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| if (chip->min_icl_usb100) { | |
| pr_debug("USB min current set to 100mA\n"); | |
| /* set input current limit to minimum (300mA) */ | |
| rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG, | |
| INPUT_CURR_LIM_MASK, | |
| INPUT_CURR_LIM_300MA); | |
| if (rc) | |
| pr_err("Couldn't set ICL mA rc=%d\n", rc); | |
| if (!(chip->workaround_flags & WRKRND_USB100_FAIL)) | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_100_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB100 rc=%d\n", | |
| rc); | |
| } else { | |
| pr_debug("USB min current set to 500mA\n"); | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_500_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB100 rc=%d\n", | |
| rc); | |
| } | |
| return rc; | |
| } | |
| static struct power_supply *get_parallel_psy(struct smb1360_chip *chip) | |
| { | |
| if (chip->parallel_psy) | |
| return chip->parallel_psy; | |
| chip->parallel_psy = power_supply_get_by_name("usb-parallel"); | |
| if (!chip->parallel_psy) | |
| pr_debug("parallel charger not found\n"); | |
| return chip->parallel_psy; | |
| } | |
| static int __smb1360_parallel_charger_enable(struct smb1360_chip *chip, | |
| bool enable) | |
| { | |
| struct power_supply *parallel_psy = get_parallel_psy(chip); | |
| union power_supply_propval pval = {0, }; | |
| if (!parallel_psy) | |
| return 0; | |
| pval.intval = (enable ? (chip->max_parallel_chg_current * 1000) : 0); | |
| parallel_psy->set_property(parallel_psy, | |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); | |
| pval.intval = (enable ? 1 : 0); | |
| parallel_psy->set_property(parallel_psy, | |
| POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); | |
| pr_debug("Parallel-charger %s max_chg_current=%d\n", | |
| enable ? "enabled" : "disabled", | |
| enable ? (chip->max_parallel_chg_current * 1000) : 0); | |
| return 0; | |
| } | |
| static int smb1360_parallel_charger_enable(struct smb1360_chip *chip, | |
| int reason, bool enable) | |
| { | |
| int disabled, *disabled_status; | |
| mutex_lock(&chip->parallel_chg_lock); | |
| disabled = chip->parallel_chg_disable_status; | |
| disabled_status = &chip->parallel_chg_disable_status; | |
| pr_debug("reason=0x%x requested=%s disabled_status=0x%x\n", | |
| reason, enable ? "enable" : "disable", disabled); | |
| if (enable == true) | |
| disabled &= ~reason; | |
| else | |
| disabled |= reason; | |
| if (*disabled_status && !disabled) | |
| __smb1360_parallel_charger_enable(chip, true); | |
| if (!(*disabled_status) && disabled) | |
| __smb1360_parallel_charger_enable(chip, false); | |
| *disabled_status = disabled; | |
| pr_debug("disabled_status = %x\n", *disabled_status); | |
| mutex_unlock(&chip->parallel_chg_lock); | |
| return 0; | |
| } | |
| static void smb1360_parallel_work(struct work_struct *work) | |
| { | |
| u8 reg; | |
| int rc, i; | |
| struct smb1360_chip *chip = container_of(work, | |
| struct smb1360_chip, parallel_work); | |
| /* check the AICL settled value */ | |
| rc = smb1360_read(chip, STATUS_1_REG, ®); | |
| if (rc) { | |
| pr_debug("Unable to read AICL status rc=%d\n", rc); | |
| goto exit_work; | |
| } | |
| pr_debug("STATUS_1 (aicl status)=0x%x\n", reg); | |
| if ((reg & AICL_CURRENT_STATUS_MASK) == AICL_LIMIT_1500MA) { | |
| /* Strong Charger - Enable parallel path */ | |
| /* find the new fastchg current */ | |
| chip->fastchg_current += (chip->max_parallel_chg_current / 2); | |
| for (i = 0; i < ARRAY_SIZE(fastchg_current) - 1; i++) { | |
| if (fastchg_current[i] >= chip->fastchg_current) | |
| break; | |
| } | |
| if (i == ARRAY_SIZE(fastchg_current)) | |
| i--; | |
| rc = smb1360_masked_write(chip, CHG_CURRENT_REG, | |
| FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT); | |
| if (rc) | |
| pr_err("Couldn't set fastchg mA rc=%d\n", rc); | |
| pr_debug("fast-chg (parallel-mode) current set to = %d\n", | |
| fastchg_current[i]); | |
| smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, true); | |
| } else { | |
| /* Weak-charger - Disable parallel path */ | |
| smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, false); | |
| } | |
| exit_work: | |
| smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL); | |
| } | |
| static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip) | |
| { | |
| int rc = 0, i, therm_ma, current_ma; | |
| int path_current = chip->usb_psy_ma; | |
| /* | |
| * If battery is absent do not modify the current at all, these | |
| * would be some appropriate values set by the bootloader or default | |
| * configuration and since it is the only source of power we should | |
| * not change it | |
| */ | |
| if (!chip->batt_present) { | |
| pr_debug("ignoring current request since battery is absent\n"); | |
| return 0; | |
| } | |
| 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 | |
| */ | |
| therm_ma = chip->thermal_mitigation[chip->therm_lvl_sel]; | |
| else | |
| therm_ma = path_current; | |
| current_ma = min(therm_ma, path_current); | |
| if (chip->workaround_flags & WRKRND_HARD_JEITA) { | |
| if (chip->batt_warm) | |
| current_ma = min(current_ma, chip->warm_bat_ma); | |
| else if (chip->batt_cool) | |
| current_ma = min(current_ma, chip->cool_bat_ma); | |
| } | |
| if (current_ma <= 2) { | |
| /* | |
| * SMB1360 does not support USB suspend - | |
| * so set the current-limit to minimum in suspend. | |
| */ | |
| pr_debug("current_ma=%d <= 2 set USB current to minimum\n", | |
| current_ma); | |
| rc = smb1360_set_minimum_usb_current(chip); | |
| if (rc < 0) | |
| pr_err("Couldn't to set minimum USB current rc = %d\n", | |
| rc); | |
| /* disable parallel charger */ | |
| if (chip->parallel_charging) | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_CURRENT, false); | |
| return rc; | |
| } | |
| for (i = ARRAY_SIZE(input_current_limit) - 1; i >= 0; i--) { | |
| if (input_current_limit[i] <= current_ma) | |
| break; | |
| } | |
| if (i < 0) { | |
| pr_debug("Couldn't find ICL mA rc=%d\n", rc); | |
| i = 0; | |
| } | |
| /* set input current limit */ | |
| rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG, | |
| INPUT_CURR_LIM_MASK, i); | |
| if (rc) | |
| pr_err("Couldn't set ICL mA rc=%d\n", rc); | |
| pr_debug("ICL set to = %d\n", input_current_limit[i]); | |
| if ((current_ma <= CURRENT_100_MA) && | |
| ((chip->workaround_flags & WRKRND_USB100_FAIL) || | |
| !chip->min_icl_usb100)) { | |
| pr_debug("usb100 not supported: usb100_wrkrnd=%d min_icl_100=%d\n", | |
| !!(chip->workaround_flags & WRKRND_USB100_FAIL), | |
| chip->min_icl_usb100); | |
| current_ma = CURRENT_500_MA; | |
| } | |
| if (current_ma <= CURRENT_100_MA) { | |
| /* USB 100 */ | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_100_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB100 rc=%d\n", rc); | |
| pr_debug("Setting USB 100\n"); | |
| } else if (current_ma <= CURRENT_500_MA) { | |
| /* USB 500 */ | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_500_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB500 rc=%d\n", rc); | |
| pr_debug("Setting USB 500\n"); | |
| } else { | |
| /* USB AC */ | |
| if (chip->rsense_10mohm) | |
| current_ma /= 2; | |
| for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) { | |
| if (fastchg_current[i] <= current_ma) | |
| break; | |
| } | |
| if (i < 0) { | |
| pr_debug("Couldn't find fastchg mA rc=%d\n", rc); | |
| i = 0; | |
| } | |
| chip->fastchg_current = fastchg_current[i]; | |
| /* set fastchg limit */ | |
| rc = smb1360_masked_write(chip, CHG_CURRENT_REG, | |
| FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT); | |
| if (rc) | |
| pr_err("Couldn't set fastchg mA rc=%d\n", rc); | |
| /* | |
| * To move to a new (higher) input-current setting, | |
| * first set USB500 and then USBAC. This makes sure | |
| * that the new ICL setting takes affect. | |
| */ | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_500_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB500 rc=%d\n", rc); | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, | |
| USB_CTRL_MASK, USB_AC_BIT); | |
| if (rc) | |
| pr_err("Couldn't configure for USB AC rc=%d\n", rc); | |
| pr_debug("fast-chg current set to = %d\n", fastchg_current[i]); | |
| } | |
| return rc; | |
| } | |
| static int smb1360_set_jeita_comp_curr(struct smb1360_chip *chip, | |
| int current_ma) | |
| { | |
| int i; | |
| int rc = 0; | |
| for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) { | |
| if (fastchg_current[i] <= current_ma) | |
| break; | |
| } | |
| if (i < 0) { | |
| pr_debug("Couldn't find fastchg_current %dmA\n", current_ma); | |
| i = 0; | |
| } | |
| rc = smb1360_masked_write(chip, CHG_CMP_CFG, | |
| JEITA_COMP_CURR_MASK, i); | |
| if (rc) | |
| pr_err("Couldn't configure for Icomp, rc = %d\n", rc); | |
| return rc; | |
| } | |
| #define TEMP_THRE_SET(x) ((x + 300) / 10) | |
| #define TEMP_THRE_GET(x) ((x * 10) - 300) | |
| static int smb1360_set_soft_jeita_threshold(struct smb1360_chip *chip, | |
| int cold_threshold, int hot_threshold) | |
| { | |
| int rc = 0; | |
| rc = smb1360_write(chip, JEITA_SOFT_COLD_REG, | |
| TEMP_THRE_SET(cold_threshold)); | |
| if (rc) { | |
| pr_err("Couldn't set soft cold threshold, rc = %d\n", rc); | |
| return rc; | |
| } else { | |
| chip->soft_cold_thresh = cold_threshold; | |
| } | |
| rc = smb1360_write(chip, JEITA_SOFT_HOT_REG, | |
| TEMP_THRE_SET(hot_threshold)); | |
| if (rc) { | |
| pr_err("Couldn't set soft hot threshold, rc = %d\n", rc); | |
| return rc; | |
| } else { | |
| chip->soft_hot_thresh = hot_threshold; | |
| } | |
| return rc; | |
| } | |
| static int smb1360_get_soft_jeita_threshold(struct smb1360_chip *chip, | |
| int *cold_threshold, int *hot_threshold) | |
| { | |
| int rc = 0; | |
| u8 value; | |
| rc = smb1360_read(chip, JEITA_SOFT_COLD_REG, &value); | |
| if (rc) { | |
| pr_err("Couldn't get soft cold threshold, rc = %d\n", rc); | |
| return rc; | |
| } | |
| *cold_threshold = TEMP_THRE_GET(value); | |
| rc = smb1360_read(chip, JEITA_SOFT_HOT_REG, &value); | |
| if (rc) { | |
| pr_err("Couldn't get soft hot threshold, rc = %d\n", rc); | |
| return rc; | |
| } | |
| *hot_threshold = TEMP_THRE_GET(value); | |
| return rc; | |
| } | |
| #define OTP_HARD_COLD_REG_ADDR 0x12 | |
| #define OTP_HARD_HOT_REG_ADDR 0x13 | |
| static int smb1360_set_otp_hard_jeita_threshold(struct smb1360_chip *chip, | |
| int cold_threshold, int hot_threshold) | |
| { | |
| int rc = 0, i; | |
| u8 reg[4] = { 0 }; | |
| u8 otp_reg = 0; | |
| int temp_code; | |
| if (cold_threshold > chip->cool_bat_decidegc || | |
| chip->cool_bat_decidegc >= chip->warm_bat_decidegc || | |
| chip->warm_bat_decidegc > hot_threshold) { | |
| pr_err("cold:%d, cool:%d, warm:%d, hot:%d should be ordered in size\n", | |
| cold_threshold, chip->cool_bat_decidegc, | |
| chip->warm_bat_decidegc, hot_threshold); | |
| return -EINVAL; | |
| } | |
| pr_debug("cold:%d, cool:%d, warm:%d, hot:%d\n", | |
| cold_threshold, chip->cool_bat_decidegc, | |
| chip->warm_bat_decidegc, hot_threshold); | |
| if (!chip->hard_jeita_otp_reg) { | |
| otp_reg = smb1360_alloc_otp_backup_register(chip, | |
| ARRAY_SIZE(reg), OTP_BACKUP_FG_USE); | |
| if (otp_reg <= 0) { | |
| pr_err("OTP reg allocation failed for hard JEITA\n"); | |
| return otp_reg; | |
| } | |
| chip->hard_jeita_otp_reg = otp_reg; | |
| } else { | |
| otp_reg = chip->hard_jeita_otp_reg; | |
| } | |
| pr_debug("hard_jeita_otp_reg = 0x%x\n", chip->hard_jeita_otp_reg); | |
| reg[0] = (u8)OTP_HARD_HOT_REG_ADDR; | |
| temp_code = TEMP_THRE_SET(hot_threshold); | |
| if (temp_code < 0) { | |
| pr_err("hard hot temp encode failed\n"); | |
| return temp_code; | |
| } | |
| reg[1] = (u8)temp_code; | |
| reg[2] = (u8)OTP_HARD_COLD_REG_ADDR; | |
| temp_code = TEMP_THRE_SET(cold_threshold); | |
| if (temp_code < 0) { | |
| pr_err("hard cold temp encode failed\n"); | |
| return temp_code; | |
| } | |
| reg[3] = (u8)temp_code; | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't request FG access rc = %d\n", rc); | |
| return rc; | |
| } | |
| chip->fg_access_type = FG_ACCESS_CFG; | |
| rc = smb1360_select_fg_i2c_address(chip); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address\n"); | |
| goto restore_fg; | |
| } | |
| for (i = 0; i < ARRAY_SIZE(reg); i++) { | |
| rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]); | |
| if (rc) { | |
| pr_err("Write FG address 0x%x: 0x%x failed, rc = %d\n", | |
| otp_reg + i, reg[i], rc); | |
| goto restore_fg; | |
| } | |
| pr_debug("Write FG addr=0x%x, value=0x%x\n", | |
| otp_reg + i, reg[i]); | |
| } | |
| rc = smb1360_otp_backup_alg_update(chip); | |
| if (rc) { | |
| pr_err("Update OTP backup algorithm failed\n"); | |
| goto restore_fg; | |
| } | |
| rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG, | |
| CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE); | |
| if (rc) { | |
| pr_err("Write reg 0x0E failed, rc = %d\n", rc); | |
| goto restore_fg; | |
| } | |
| restore_fg: | |
| rc = smb1360_disable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't disable FG access rc = %d\n", rc); | |
| return rc; | |
| } | |
| return rc; | |
| } | |
| static int smb1360_hard_jeita_otp_init(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| if (!chip->otp_hard_jeita_config) | |
| return rc; | |
| rc = smb1360_set_otp_hard_jeita_threshold(chip, | |
| chip->otp_cold_bat_decidegc, chip->otp_hot_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set OTP hard jeita threshold,rc = %d\n", rc); | |
| return rc; | |
| } | |
| return rc; | |
| } | |
| static int smb1360_system_temp_level_set(struct smb1360_chip *chip, | |
| int lvl_sel) | |
| { | |
| int rc = 0; | |
| int prev_therm_lvl; | |
| if (!chip->thermal_mitigation) { | |
| pr_err("Thermal mitigation not supported\n"); | |
| return -EINVAL; | |
| } | |
| if (lvl_sel < 0) { | |
| pr_err("Unsupported level selected %d\n", lvl_sel); | |
| return -EINVAL; | |
| } | |
| if (lvl_sel >= chip->thermal_levels) { | |
| pr_err("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)) { | |
| rc = smb1360_set_minimum_usb_current(chip); | |
| if (rc) | |
| pr_err("Couldn't set USB current to minimum rc = %d\n", | |
| rc); | |
| } else { | |
| rc = smb1360_set_appropriate_usb_current(chip); | |
| if (rc) | |
| pr_err("Couldn't set USB current rc = %d\n", rc); | |
| } | |
| mutex_unlock(&chip->current_change_lock); | |
| return rc; | |
| } | |
| static int smb1360_battery_set_property(struct power_supply *psy, | |
| enum power_supply_property prop, | |
| const union power_supply_propval *val) | |
| { | |
| struct smb1360_chip *chip = container_of(psy, | |
| struct smb1360_chip, batt_psy); | |
| switch (prop) { | |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: | |
| smb1360_charging_disable(chip, USER, !val->intval); | |
| if (chip->parallel_charging) | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_USER, val->intval); | |
| power_supply_changed(&chip->batt_psy); | |
| power_supply_changed(chip->usb_psy); | |
| break; | |
| case POWER_SUPPLY_PROP_CAPACITY: | |
| chip->fake_battery_soc = val->intval; | |
| pr_info("fake_soc set to %d\n", chip->fake_battery_soc); | |
| power_supply_changed(&chip->batt_psy); | |
| break; | |
| case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: | |
| smb1360_system_temp_level_set(chip, val->intval); | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static int smb1360_battery_is_writeable(struct power_supply *psy, | |
| enum power_supply_property prop) | |
| { | |
| int rc; | |
| switch (prop) { | |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: | |
| case POWER_SUPPLY_PROP_CAPACITY: | |
| case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: | |
| rc = 1; | |
| break; | |
| default: | |
| rc = 0; | |
| break; | |
| } | |
| return rc; | |
| } | |
| static int smb1360_battery_get_property(struct power_supply *psy, | |
| enum power_supply_property prop, | |
| union power_supply_propval *val) | |
| { | |
| struct smb1360_chip *chip = container_of(psy, | |
| struct smb1360_chip, batt_psy); | |
| switch (prop) { | |
| case POWER_SUPPLY_PROP_HEALTH: | |
| val->intval = smb1360_get_prop_batt_health(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_PRESENT: | |
| val->intval = smb1360_get_prop_batt_present(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_STATUS: | |
| val->intval = smb1360_get_prop_batt_status(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: | |
| val->intval = !chip->charging_disabled_status; | |
| break; | |
| case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
| val->intval = smb1360_get_prop_charge_type(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_CAPACITY: | |
| val->intval = smb1360_get_prop_batt_capacity(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | |
| val->intval = smb1360_get_prop_chg_full_design(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_VOLTAGE_NOW: | |
| val->intval = smb1360_get_prop_voltage_now(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_CURRENT_NOW: | |
| val->intval = smb1360_get_prop_current_now(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_RESISTANCE: | |
| val->intval = smb1360_get_prop_batt_resistance(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_TEMP: | |
| val->intval = smb1360_get_prop_batt_temp(chip); | |
| break; | |
| case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: | |
| val->intval = chip->therm_lvl_sel; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static void smb1360_external_power_changed(struct power_supply *psy) | |
| { | |
| struct smb1360_chip *chip = container_of(psy, | |
| struct smb1360_chip, batt_psy); | |
| union power_supply_propval prop = {0,}; | |
| int rc, current_limit = 0; | |
| rc = chip->usb_psy->get_property(chip->usb_psy, | |
| POWER_SUPPLY_PROP_CURRENT_MAX, &prop); | |
| if (rc < 0) | |
| dev_err(chip->dev, | |
| "could not read USB current_max property, rc=%d\n", rc); | |
| else | |
| current_limit = prop.intval / 1000; | |
| pr_debug("current_limit = %d\n", current_limit); | |
| if (chip->usb_psy_ma != current_limit) { | |
| mutex_lock(&chip->current_change_lock); | |
| chip->usb_psy_ma = current_limit; | |
| rc = smb1360_set_appropriate_usb_current(chip); | |
| if (rc < 0) | |
| pr_err("Couldn't set usb current rc = %d\n", rc); | |
| mutex_unlock(&chip->current_change_lock); | |
| } | |
| rc = chip->usb_psy->get_property(chip->usb_psy, | |
| POWER_SUPPLY_PROP_ONLINE, &prop); | |
| if (rc < 0) | |
| pr_err("could not read USB ONLINE property, rc=%d\n", rc); | |
| /* update online property */ | |
| rc = 0; | |
| if (chip->usb_present && !chip->charging_disabled_status | |
| && chip->usb_psy_ma != 0) { | |
| if (prop.intval == 0) | |
| rc = power_supply_set_online(chip->usb_psy, true); | |
| } else { | |
| if (prop.intval == 1) | |
| rc = power_supply_set_online(chip->usb_psy, false); | |
| } | |
| if (rc < 0) | |
| pr_err("could not set usb online, rc=%d\n", rc); | |
| } | |
| static int hot_hard_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| chip->batt_hot = !!rt_stat; | |
| if (chip->parallel_charging) { | |
| pr_debug("%s parallel-charging\n", chip->batt_hot ? | |
| "Disable" : "Enable"); | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_JEITA_HARD, !chip->batt_hot); | |
| } | |
| if (chip->hot_hysteresis) { | |
| smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_JEITA_HYSTERSIS); | |
| schedule_work(&chip->jeita_hysteresis_work); | |
| } | |
| return 0; | |
| } | |
| static int cold_hard_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| chip->batt_cold = !!rt_stat; | |
| if (chip->parallel_charging) { | |
| pr_debug("%s parallel-charging\n", chip->batt_cold ? | |
| "Disable" : "Enable"); | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_JEITA_HARD, !chip->batt_cold); | |
| } | |
| if (chip->cold_hysteresis) { | |
| smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_JEITA_HYSTERSIS); | |
| schedule_work(&chip->jeita_hysteresis_work); | |
| } | |
| return 0; | |
| } | |
| static void smb1360_jeita_hysteresis_work(struct work_struct *work) | |
| { | |
| int rc = 0; | |
| int hard_hot, hard_cold; | |
| struct smb1360_chip *chip = container_of(work, | |
| struct smb1360_chip, jeita_hysteresis_work); | |
| /* disable hard JEITA IRQ first */ | |
| rc = smb1360_masked_write(chip, IRQ_CFG_REG, | |
| IRQ_BAT_HOT_COLD_HARD_BIT, 0); | |
| if (rc) { | |
| pr_err("disable hard JEITA IRQ failed, rc = %d\n", rc); | |
| goto exit_worker; | |
| } | |
| hard_hot = chip->otp_hot_bat_decidegc; | |
| hard_cold = chip->otp_cold_bat_decidegc; | |
| if (chip->batt_hot) | |
| hard_hot -= chip->hot_hysteresis; | |
| else if (chip->batt_cold) | |
| hard_cold += chip->cold_hysteresis; | |
| rc = smb1360_set_otp_hard_jeita_threshold(chip, hard_cold, hard_hot); | |
| if (rc) { | |
| pr_err("set hard JEITA threshold failed\n"); | |
| goto exit_worker; | |
| } | |
| pr_debug("hard cold: %d, hard hot: %d reprogramed\n", | |
| hard_cold, hard_hot); | |
| /* enable hard JEITA IRQ at the end */ | |
| rc = smb1360_masked_write(chip, IRQ_CFG_REG, | |
| IRQ_BAT_HOT_COLD_HARD_BIT, IRQ_BAT_HOT_COLD_HARD_BIT); | |
| if (rc) | |
| pr_err("enable hard JEITA IRQ failed\n"); | |
| exit_worker: | |
| smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_HYSTERSIS); | |
| } | |
| /* | |
| * This worker thread should only be called when WRKRND_HARD_JEITA | |
| * is set. | |
| * It is needed to re-program JEITA soft thresholds, compensate | |
| * target voltage and charging current manually. | |
| * The function is required as JEITA hard thresholds can't be programmed. | |
| */ | |
| static void smb1360_jeita_work_fn(struct work_struct *work) | |
| { | |
| int temp; | |
| int rc = 0; | |
| struct delayed_work *dwork = to_delayed_work(work); | |
| struct smb1360_chip *chip = container_of(dwork, struct smb1360_chip, | |
| jeita_work); | |
| temp = smb1360_get_prop_batt_temp(chip); | |
| if (temp > chip->hot_bat_decidegc) { | |
| /* battery status is hot, only config thresholds */ | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->warm_bat_decidegc, chip->hot_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set jeita threshold\n"); | |
| goto end; | |
| } | |
| } else if (temp > chip->warm_bat_decidegc || | |
| (temp == chip->warm_bat_decidegc && !!chip->soft_hot_rt_stat)) { | |
| /* battery status is warm, do compensation manually */ | |
| chip->batt_warm = true; | |
| chip->batt_cool = false; | |
| rc = smb1360_float_voltage_set(chip, chip->warm_bat_mv); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set float voltage\n"); | |
| goto end; | |
| } | |
| rc = smb1360_set_appropriate_usb_current(chip); | |
| if (rc) | |
| pr_err("Couldn't set USB current\n"); | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->warm_bat_decidegc, chip->hot_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set jeita threshold\n"); | |
| goto end; | |
| } | |
| } else if (temp > chip->cool_bat_decidegc || | |
| (temp == chip->cool_bat_decidegc && !chip->soft_cold_rt_stat)) { | |
| /* battery status is good, do the normal charging */ | |
| chip->batt_warm = false; | |
| chip->batt_cool = false; | |
| rc = smb1360_float_voltage_set(chip, chip->vfloat_mv); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set float voltage\n"); | |
| goto end; | |
| } | |
| rc = smb1360_set_appropriate_usb_current(chip); | |
| if (rc) | |
| pr_err("Couldn't set USB current\n"); | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cool_bat_decidegc, chip->warm_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set jeita threshold\n"); | |
| goto end; | |
| } | |
| } else if (temp > chip->cold_bat_decidegc) { | |
| /* battery status is cool, do compensation manually */ | |
| chip->batt_cool = true; | |
| chip->batt_warm = false; | |
| rc = smb1360_float_voltage_set(chip, chip->cool_bat_mv); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set float voltage\n"); | |
| goto end; | |
| } | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cold_bat_decidegc, chip->cool_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set jeita threshold\n"); | |
| goto end; | |
| } | |
| } else { | |
| /* battery status is cold, only config thresholds */ | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cold_bat_decidegc, chip->cool_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set jeita threshold\n"); | |
| goto end; | |
| } | |
| } | |
| pr_debug("warm %d, cool %d, soft_cold_rt_sts %d, soft_hot_rt_sts %d, jeita supported %d, threshold_now %d %d\n", | |
| chip->batt_warm, chip->batt_cool, !!chip->soft_cold_rt_stat, | |
| !!chip->soft_hot_rt_stat, chip->soft_jeita_supported, | |
| chip->soft_cold_thresh, chip->soft_hot_thresh); | |
| end: | |
| smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_SOFT); | |
| } | |
| static int hot_soft_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| chip->soft_hot_rt_stat = rt_stat; | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| if (!chip->config_hard_thresholds) | |
| chip->batt_warm = !!rt_stat; | |
| if (chip->workaround_flags & WRKRND_HARD_JEITA) { | |
| cancel_delayed_work_sync(&chip->jeita_work); | |
| schedule_delayed_work(&chip->jeita_work, | |
| msecs_to_jiffies(JEITA_WORK_MS)); | |
| smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_JEITA_SOFT); | |
| } | |
| if (chip->parallel_charging) { | |
| pr_debug("%s parallel-charging\n", chip->batt_warm ? | |
| "Disable" : "Enable"); | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_JEITA_SOFT, !chip->batt_warm); | |
| } | |
| return 0; | |
| } | |
| static int cold_soft_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| chip->soft_cold_rt_stat = rt_stat; | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| if (!chip->config_hard_thresholds) | |
| chip->batt_cool = !!rt_stat; | |
| if (chip->workaround_flags & WRKRND_HARD_JEITA) { | |
| cancel_delayed_work_sync(&chip->jeita_work); | |
| schedule_delayed_work(&chip->jeita_work, | |
| msecs_to_jiffies(JEITA_WORK_MS)); | |
| smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_JEITA_SOFT); | |
| } | |
| if (chip->parallel_charging) { | |
| pr_debug("%s parallel-charging\n", chip->batt_cool ? | |
| "Disable" : "Enable"); | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_JEITA_SOFT, !chip->batt_cool); | |
| } | |
| return 0; | |
| } | |
| static int battery_missing_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| chip->batt_present = !rt_stat; | |
| return 0; | |
| } | |
| static int vbat_low_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("vbat low\n"); | |
| return 0; | |
| } | |
| static int chg_hot_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_warn_ratelimited("chg hot\n"); | |
| return 0; | |
| } | |
| static int chg_term_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| chip->batt_full = !!rt_stat; | |
| if (chip->parallel_charging) { | |
| pr_debug("%s parallel-charging\n", chip->batt_full ? | |
| "Disable" : "Enable"); | |
| smb1360_parallel_charger_enable(chip, | |
| PARALLEL_EOC, !chip->batt_full); | |
| } | |
| return 0; | |
| } | |
| static int chg_fastchg_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| return 0; | |
| } | |
| static int usbin_uv_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| bool usb_present = !rt_stat; | |
| pr_debug("chip->usb_present = %d usb_present = %d\n", | |
| chip->usb_present, usb_present); | |
| if (chip->usb_present && !usb_present) { | |
| /* USB removed */ | |
| chip->usb_present = usb_present; | |
| power_supply_set_present(chip->usb_psy, usb_present); | |
| } | |
| if (!chip->usb_present && usb_present) { | |
| /* USB inserted */ | |
| chip->usb_present = usb_present; | |
| power_supply_set_present(chip->usb_psy, usb_present); | |
| } | |
| return 0; | |
| } | |
| static int aicl_done_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| bool aicl_done = !!rt_stat; | |
| pr_debug("AICL done=%d\n", aicl_done); | |
| if (chip->parallel_charging && aicl_done) { | |
| cancel_work_sync(&chip->parallel_work); | |
| smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL); | |
| schedule_work(&chip->parallel_work); | |
| } | |
| return 0; | |
| } | |
| static int chg_inhibit_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| /* | |
| * charger is inserted when the battery voltage is high | |
| * so h/w won't start charging just yet. Treat this as | |
| * battery full | |
| */ | |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); | |
| chip->batt_full = !!rt_stat; | |
| return 0; | |
| } | |
| static int delta_soc_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("SOC changed! - rt_stat = 0x%02x\n", rt_stat); | |
| return 0; | |
| } | |
| static int min_soc_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("SOC dropped below min SOC, rt_stat = 0x%02x\n", rt_stat); | |
| if (chip->awake_min_soc) | |
| rt_stat ? smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_MIN_SOC) : | |
| smb1360_relax(&chip->smb1360_ws, | |
| WAKEUP_SRC_MIN_SOC); | |
| return 0; | |
| } | |
| static int empty_soc_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("SOC empty! rt_stat = 0x%02x\n", rt_stat); | |
| if (!chip->empty_soc_disabled) { | |
| if (rt_stat) { | |
| chip->empty_soc = true; | |
| smb1360_stay_awake(&chip->smb1360_ws, | |
| WAKEUP_SRC_EMPTY_SOC); | |
| pr_warn_ratelimited("SOC is 0\n"); | |
| } else { | |
| chip->empty_soc = false; | |
| smb1360_relax(&chip->smb1360_ws, | |
| WAKEUP_SRC_EMPTY_SOC); | |
| } | |
| } | |
| return 0; | |
| } | |
| static int full_soc_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| if (rt_stat) | |
| pr_debug("SOC is 100\n"); | |
| return 0; | |
| } | |
| static int fg_access_allowed_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("stat=%d\n", !!rt_stat); | |
| if (rt_stat & FG_ACCESS_ALLOWED_BIT) { | |
| pr_debug("FG access granted\n"); | |
| complete_all(&chip->fg_mem_access_granted); | |
| } | |
| return 0; | |
| } | |
| static int batt_id_complete_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| pr_debug("batt_id = %x\n", (rt_stat & BATT_ID_RESULT_BIT) | |
| >> BATT_ID_SHIFT); | |
| return 0; | |
| } | |
| static int smb1360_adjust_current_gain(struct smb1360_chip *chip, | |
| int gain_factor) | |
| { | |
| int i, rc; | |
| int64_t current_gain, new_current_gain; | |
| u16 reg_value1 = 0, reg_value2 = 0; | |
| u8 reg[4] = {0x1D, 0x00, 0x1E, 0x00}; | |
| u8 otp_reg = 0; | |
| if (!chip->current_gain_otp_reg) { | |
| otp_reg = smb1360_alloc_otp_backup_register(chip, | |
| ARRAY_SIZE(reg), OTP_BACKUP_FG_USE); | |
| if (otp_reg <= 0) { | |
| pr_err("OTP reg allocation fail for adjusting current gain\n"); | |
| return otp_reg; | |
| } else { | |
| chip->current_gain_otp_reg = otp_reg; | |
| } | |
| } else { | |
| otp_reg = chip->current_gain_otp_reg; | |
| } | |
| pr_debug("current_gain_otp_reg = 0x%x\n", chip->current_gain_otp_reg); | |
| if (gain_factor) { | |
| rc = smb1360_fg_read(chip, CURRENT_GAIN_LSB_REG, ®[1]); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| rc = smb1360_fg_read(chip, CURRENT_GAIN_MSB_REG, ®[3]); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| reg_value1 = (reg[3] << 8) | reg[1]; | |
| current_gain = float_decode(reg_value1); | |
| new_current_gain = MICRO_UNIT + (gain_factor * current_gain); | |
| reg_value2 = float_encode(new_current_gain); | |
| reg[1] = reg_value2 & 0xFF; | |
| reg[3] = (reg_value2 & 0xFF00) >> 8; | |
| pr_debug("current_gain_reg=0x%x current_gain_decoded=%lld new_current_gain_decoded=%lld new_current_gain_reg=0x%x\n", | |
| reg_value1, current_gain, new_current_gain, reg_value2); | |
| for (i = 0; i < ARRAY_SIZE(reg); i++) { | |
| pr_debug("Writing reg_add=%x value=%x\n", | |
| otp_reg + i, reg[i]); | |
| rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]); | |
| if (rc) { | |
| pr_err("Write FG address 0x%x failed, rc = %d\n", | |
| otp_reg + i, rc); | |
| return rc; | |
| } | |
| } | |
| rc = smb1360_otp_backup_alg_update(chip); | |
| if (rc) { | |
| pr_err("Update OTP backup algorithm failed\n"); | |
| return rc; | |
| } | |
| } else { | |
| pr_debug("Disabling gain correction\n"); | |
| rc = smb1360_fg_write(chip, 0xF0, 0x00); | |
| if (rc) { | |
| pr_err("Write fg address 0x%x failed, rc = %d\n", | |
| 0xF0, rc); | |
| return rc; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int smb1360_otp_gain_config(struct smb1360_chip *chip, int gain_factor) | |
| { | |
| int rc = 0; | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't request FG access rc = %d\n", rc); | |
| return rc; | |
| } | |
| chip->fg_access_type = FG_ACCESS_CFG; | |
| rc = smb1360_select_fg_i2c_address(chip); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address\n"); | |
| goto restore_fg; | |
| } | |
| rc = smb1360_adjust_current_gain(chip, gain_factor); | |
| if (rc) { | |
| pr_err("Unable to modify current gain rc=%d\n", rc); | |
| goto restore_fg; | |
| } | |
| rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG, | |
| CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE); | |
| if (rc) { | |
| pr_err("Write reg 0x0E failed, rc = %d\n", rc); | |
| goto restore_fg; | |
| } | |
| restore_fg: | |
| rc = smb1360_disable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't disable FG access rc = %d\n", rc); | |
| return rc; | |
| } | |
| return rc; | |
| } | |
| static int smb1360_otg_disable(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT, 0); | |
| if (rc) { | |
| pr_err("Couldn't disable OTG mode rc=%d\n", rc); | |
| return rc; | |
| } | |
| mutex_lock(&chip->otp_gain_lock); | |
| /* Disable current gain configuration */ | |
| if (chip->otg_fet_present && chip->fet_gain_enabled) { | |
| /* Disable FET */ | |
| gpio_set_value(chip->otg_fet_enable_gpio, 1); | |
| rc = smb1360_otp_gain_config(chip, 0); | |
| if (rc < 0) | |
| pr_err("Couldn't config OTP gain config rc=%d\n", rc); | |
| else | |
| chip->fet_gain_enabled = false; | |
| } | |
| mutex_unlock(&chip->otp_gain_lock); | |
| return rc; | |
| } | |
| static int otg_fail_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| int rc; | |
| pr_debug("OTG Failed stat=%d\n", rt_stat); | |
| rc = smb1360_otg_disable(chip); | |
| if (rc) | |
| pr_err("Couldn't disable OTG mode rc=%d\n", rc); | |
| return 0; | |
| } | |
| static int otg_oc_handler(struct smb1360_chip *chip, u8 rt_stat) | |
| { | |
| int rc; | |
| pr_debug("OTG over-current stat=%d\n", rt_stat); | |
| rc = smb1360_otg_disable(chip); | |
| if (rc) | |
| pr_err("Couldn't disable OTG mode rc=%d\n", rc); | |
| return 0; | |
| } | |
| struct smb_irq_info { | |
| const char *name; | |
| int (*smb_irq)(struct smb1360_chip *chip, | |
| u8 rt_stat); | |
| int high; | |
| int low; | |
| }; | |
| struct irq_handler_info { | |
| u8 stat_reg; | |
| u8 val; | |
| u8 prev_val; | |
| struct smb_irq_info irq_info[4]; | |
| }; | |
| static struct irq_handler_info handlers[] = { | |
| {IRQ_A_REG, 0, 0, | |
| { | |
| { | |
| .name = "cold_soft", | |
| .smb_irq = cold_soft_handler, | |
| }, | |
| { | |
| .name = "hot_soft", | |
| .smb_irq = hot_soft_handler, | |
| }, | |
| { | |
| .name = "cold_hard", | |
| .smb_irq = cold_hard_handler, | |
| }, | |
| { | |
| .name = "hot_hard", | |
| .smb_irq = hot_hard_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_B_REG, 0, 0, | |
| { | |
| { | |
| .name = "chg_hot", | |
| .smb_irq = chg_hot_handler, | |
| }, | |
| { | |
| .name = "vbat_low", | |
| .smb_irq = vbat_low_handler, | |
| }, | |
| { | |
| .name = "battery_missing", | |
| .smb_irq = battery_missing_handler, | |
| }, | |
| { | |
| .name = "battery_missing", | |
| .smb_irq = battery_missing_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_C_REG, 0, 0, | |
| { | |
| { | |
| .name = "chg_term", | |
| .smb_irq = chg_term_handler, | |
| }, | |
| { | |
| .name = "taper", | |
| }, | |
| { | |
| .name = "recharge", | |
| }, | |
| { | |
| .name = "fast_chg", | |
| .smb_irq = chg_fastchg_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_D_REG, 0, 0, | |
| { | |
| { | |
| .name = "prechg_timeout", | |
| }, | |
| { | |
| .name = "safety_timeout", | |
| }, | |
| { | |
| .name = "aicl_done", | |
| .smb_irq = aicl_done_handler, | |
| }, | |
| { | |
| .name = "battery_ov", | |
| }, | |
| }, | |
| }, | |
| {IRQ_E_REG, 0, 0, | |
| { | |
| { | |
| .name = "usbin_uv", | |
| .smb_irq = usbin_uv_handler, | |
| }, | |
| { | |
| .name = "usbin_ov", | |
| }, | |
| { | |
| .name = "unused", | |
| }, | |
| { | |
| .name = "chg_inhibit", | |
| .smb_irq = chg_inhibit_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_F_REG, 0, 0, | |
| { | |
| { | |
| .name = "power_ok", | |
| }, | |
| { | |
| .name = "unused", | |
| }, | |
| { | |
| .name = "otg_fail", | |
| .smb_irq = otg_fail_handler, | |
| }, | |
| { | |
| .name = "otg_oc", | |
| .smb_irq = otg_oc_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_G_REG, 0, 0, | |
| { | |
| { | |
| .name = "delta_soc", | |
| .smb_irq = delta_soc_handler, | |
| }, | |
| { | |
| .name = "chg_error", | |
| }, | |
| { | |
| .name = "wd_timeout", | |
| }, | |
| { | |
| .name = "unused", | |
| }, | |
| }, | |
| }, | |
| {IRQ_H_REG, 0, 0, | |
| { | |
| { | |
| .name = "min_soc", | |
| .smb_irq = min_soc_handler, | |
| }, | |
| { | |
| .name = "max_soc", | |
| }, | |
| { | |
| .name = "empty_soc", | |
| .smb_irq = empty_soc_handler, | |
| }, | |
| { | |
| .name = "full_soc", | |
| .smb_irq = full_soc_handler, | |
| }, | |
| }, | |
| }, | |
| {IRQ_I_REG, 0, 0, | |
| { | |
| { | |
| .name = "fg_access_allowed", | |
| .smb_irq = fg_access_allowed_handler, | |
| }, | |
| { | |
| .name = "fg_data_recovery", | |
| }, | |
| { | |
| .name = "batt_id_complete", | |
| .smb_irq = batt_id_complete_handler, | |
| }, | |
| }, | |
| }, | |
| }; | |
| #define IRQ_LATCHED_MASK 0x02 | |
| #define IRQ_STATUS_MASK 0x01 | |
| #define BATT_ID_LATCHED_MASK 0x08 | |
| #define BATT_ID_STATUS_MASK 0x07 | |
| #define BITS_PER_IRQ 2 | |
| static irqreturn_t smb1360_stat_handler(int irq, void *dev_id) | |
| { | |
| struct smb1360_chip *chip = dev_id; | |
| int i, j; | |
| u8 triggered; | |
| u8 changed; | |
| u8 rt_stat, prev_rt_stat, irq_latched_mask, irq_status_mask; | |
| int rc; | |
| int handler_count = 0; | |
| mutex_lock(&chip->irq_complete); | |
| chip->irq_waiting = true; | |
| if (!chip->resume_completed) { | |
| dev_dbg(chip->dev, "IRQ triggered before device-resume\n"); | |
| if (!chip->irq_disabled) { | |
| disable_irq_nosync(irq); | |
| chip->irq_disabled = true; | |
| } | |
| mutex_unlock(&chip->irq_complete); | |
| return IRQ_HANDLED; | |
| } | |
| chip->irq_waiting = false; | |
| for (i = 0; i < ARRAY_SIZE(handlers); i++) { | |
| rc = smb1360_read(chip, handlers[i].stat_reg, | |
| &handlers[i].val); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read %d rc = %d\n", | |
| handlers[i].stat_reg, rc); | |
| continue; | |
| } | |
| for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) { | |
| if (handlers[i].stat_reg == IRQ_I_REG && j == 2) { | |
| irq_latched_mask = BATT_ID_LATCHED_MASK; | |
| irq_status_mask = BATT_ID_STATUS_MASK; | |
| } else { | |
| irq_latched_mask = IRQ_LATCHED_MASK; | |
| irq_status_mask = IRQ_STATUS_MASK; | |
| } | |
| triggered = handlers[i].val | |
| & (irq_latched_mask << (j * BITS_PER_IRQ)); | |
| rt_stat = handlers[i].val | |
| & (irq_status_mask << (j * BITS_PER_IRQ)); | |
| prev_rt_stat = handlers[i].prev_val | |
| & (irq_status_mask << (j * BITS_PER_IRQ)); | |
| changed = prev_rt_stat ^ rt_stat; | |
| if (triggered || changed) | |
| rt_stat ? handlers[i].irq_info[j].high++ : | |
| handlers[i].irq_info[j].low++; | |
| if ((triggered || changed) | |
| && handlers[i].irq_info[j].smb_irq != NULL) { | |
| handler_count++; | |
| rc = handlers[i].irq_info[j].smb_irq(chip, | |
| rt_stat); | |
| if (rc < 0) | |
| dev_err(chip->dev, | |
| "Couldn't handle %d irq for reg 0x%02x rc = %d\n", | |
| j, handlers[i].stat_reg, rc); | |
| } | |
| } | |
| handlers[i].prev_val = handlers[i].val; | |
| } | |
| pr_debug("handler count = %d\n", handler_count); | |
| if (handler_count) | |
| power_supply_changed(&chip->batt_psy); | |
| mutex_unlock(&chip->irq_complete); | |
| return IRQ_HANDLED; | |
| } | |
| static int show_irq_count(struct seq_file *m, void *data) | |
| { | |
| int i, j, total = 0; | |
| for (i = 0; i < ARRAY_SIZE(handlers); i++) | |
| for (j = 0; j < 4; j++) { | |
| if (!handlers[i].irq_info[j].name) | |
| continue; | |
| seq_printf(m, "%s=%d\t(high=%d low=%d)\n", | |
| handlers[i].irq_info[j].name, | |
| handlers[i].irq_info[j].high | |
| + handlers[i].irq_info[j].low, | |
| handlers[i].irq_info[j].high, | |
| handlers[i].irq_info[j].low); | |
| total += (handlers[i].irq_info[j].high | |
| + handlers[i].irq_info[j].low); | |
| } | |
| seq_printf(m, "\n\tTotal = %d\n", total); | |
| return 0; | |
| } | |
| static int irq_count_debugfs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_irq_count, chip); | |
| } | |
| static const struct file_operations irq_count_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = irq_count_debugfs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| static int get_reg(void *data, u64 *val) | |
| { | |
| struct smb1360_chip *chip = data; | |
| int rc; | |
| u8 temp; | |
| rc = smb1360_read(chip, chip->peek_poke_address, &temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't read reg %x rc = %d\n", | |
| chip->peek_poke_address, rc); | |
| return -EAGAIN; | |
| } | |
| *val = temp; | |
| return 0; | |
| } | |
| static int set_reg(void *data, u64 val) | |
| { | |
| struct smb1360_chip *chip = data; | |
| int rc; | |
| u8 temp; | |
| temp = (u8) val; | |
| rc = smb1360_write(chip, chip->peek_poke_address, temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't write 0x%02x to 0x%02x rc= %d\n", | |
| chip->peek_poke_address, temp, rc); | |
| return -EAGAIN; | |
| } | |
| return 0; | |
| } | |
| DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n"); | |
| static int fg_get_reg(void *data, u64 *val) | |
| { | |
| struct smb1360_chip *chip = data; | |
| int rc; | |
| u8 temp; | |
| rc = smb1360_select_fg_i2c_address(chip); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address\n"); | |
| return -EINVAL; | |
| } | |
| rc = smb1360_fg_read(chip, chip->fg_peek_poke_address, &temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't read reg %x rc = %d\n", | |
| chip->fg_peek_poke_address, rc); | |
| return -EAGAIN; | |
| } | |
| *val = temp; | |
| return 0; | |
| } | |
| static int fg_set_reg(void *data, u64 val) | |
| { | |
| struct smb1360_chip *chip = data; | |
| int rc; | |
| u8 temp; | |
| rc = smb1360_select_fg_i2c_address(chip); | |
| if (rc) { | |
| pr_err("Unable to set FG access I2C address\n"); | |
| return -EINVAL; | |
| } | |
| temp = (u8) val; | |
| rc = smb1360_fg_write(chip, chip->fg_peek_poke_address, temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't write 0x%02x to 0x%02x rc= %d\n", | |
| chip->fg_peek_poke_address, temp, rc); | |
| return -EAGAIN; | |
| } | |
| return 0; | |
| } | |
| DEFINE_SIMPLE_ATTRIBUTE(fg_poke_poke_debug_ops, fg_get_reg, | |
| fg_set_reg, "0x%02llx\n"); | |
| #define LAST_CNFG_REG 0x17 | |
| static int show_cnfg_regs(struct seq_file *m, void *data) | |
| { | |
| struct smb1360_chip *chip = m->private; | |
| int rc; | |
| u8 reg; | |
| u8 addr; | |
| for (addr = 0; addr <= LAST_CNFG_REG; addr++) { | |
| rc = smb1360_read(chip, addr, ®); | |
| if (!rc) | |
| seq_printf(m, "0x%02x = 0x%02x\n", addr, reg); | |
| } | |
| return 0; | |
| } | |
| static int cnfg_debugfs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_cnfg_regs, chip); | |
| } | |
| static const struct file_operations cnfg_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = cnfg_debugfs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| #define FIRST_CMD_REG 0x40 | |
| #define LAST_CMD_REG 0x42 | |
| static int show_cmd_regs(struct seq_file *m, void *data) | |
| { | |
| struct smb1360_chip *chip = m->private; | |
| int rc; | |
| u8 reg; | |
| u8 addr; | |
| for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) { | |
| rc = smb1360_read(chip, addr, ®); | |
| if (!rc) | |
| seq_printf(m, "0x%02x = 0x%02x\n", addr, reg); | |
| } | |
| return 0; | |
| } | |
| static int cmd_debugfs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_cmd_regs, chip); | |
| } | |
| static const struct file_operations cmd_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = cmd_debugfs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| #define FIRST_STATUS_REG 0x48 | |
| #define LAST_STATUS_REG 0x4B | |
| static int show_status_regs(struct seq_file *m, void *data) | |
| { | |
| struct smb1360_chip *chip = m->private; | |
| int rc; | |
| u8 reg; | |
| u8 addr; | |
| for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) { | |
| rc = smb1360_read(chip, addr, ®); | |
| if (!rc) | |
| seq_printf(m, "0x%02x = 0x%02x\n", addr, reg); | |
| } | |
| return 0; | |
| } | |
| static int status_debugfs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_status_regs, chip); | |
| } | |
| static const struct file_operations status_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = status_debugfs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| #define FIRST_IRQ_REG 0x50 | |
| #define LAST_IRQ_REG 0x58 | |
| static int show_irq_stat_regs(struct seq_file *m, void *data) | |
| { | |
| struct smb1360_chip *chip = m->private; | |
| int rc; | |
| u8 reg; | |
| u8 addr; | |
| for (addr = FIRST_IRQ_REG; addr <= LAST_IRQ_REG; addr++) { | |
| rc = smb1360_read(chip, addr, ®); | |
| if (!rc) | |
| seq_printf(m, "0x%02x = 0x%02x\n", addr, reg); | |
| } | |
| return 0; | |
| } | |
| static int irq_stat_debugfs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_irq_stat_regs, chip); | |
| } | |
| static const struct file_operations irq_stat_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = irq_stat_debugfs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| static int data_8(u8 *reg) | |
| { | |
| return reg[0]; | |
| } | |
| static int data_16(u8 *reg) | |
| { | |
| return (reg[1] << 8) | reg[0]; | |
| } | |
| static int data_24(u8 *reg) | |
| { | |
| return (reg[2] << 16) | (reg[1] << 8) | reg[0]; | |
| } | |
| static int data_28(u8 *reg) | |
| { | |
| return ((reg[3] & 0xF) << 24) | (reg[2] << 16) | | |
| (reg[1] << 8) | reg[0]; | |
| } | |
| static int data_32(u8 *reg) | |
| { | |
| return (reg[3] << 24) | (reg[2] << 16) | | |
| (reg[1] << 8) | reg[0]; | |
| } | |
| struct fg_regs { | |
| int index; | |
| int length; | |
| char *param_name; | |
| int (*calc_func) (u8 *); | |
| }; | |
| static struct fg_regs fg_scratch_pad[] = { | |
| {0, 2, "v_current_predicted", data_16}, | |
| {2, 2, "v_cutoff_predicted", data_16}, | |
| {4, 2, "v_full_predicted", data_16}, | |
| {6, 2, "ocv_estimate", data_16}, | |
| {8, 2, "rslow_drop", data_16}, | |
| {10, 2, "voltage_old", data_16}, | |
| {12, 2, "current_old", data_16}, | |
| {14, 4, "current_average_full", data_32}, | |
| {18, 2, "temperature", data_16}, | |
| {20, 2, "temp_last_track", data_16}, | |
| {22, 2, "ESR_nominal", data_16}, | |
| {26, 2, "Rslow", data_16}, | |
| {28, 2, "counter_imptr", data_16}, | |
| {30, 2, "counter_pulse", data_16}, | |
| {32, 1, "IRQ_delta_prev", data_8}, | |
| {33, 1, "cap_learning_counter", data_8}, | |
| {34, 4, "Vact_int_error", data_32}, | |
| {38, 3, "SOC_cutoff", data_24}, | |
| {41, 3, "SOC_full", data_24}, | |
| {44, 3, "SOC_auto_rechrge_temp", data_24}, | |
| {47, 3, "Battery_SOC", data_24}, | |
| {50, 4, "CC_SOC", data_28}, | |
| {54, 2, "SOC_filtered", data_16}, | |
| {56, 2, "SOC_Monotonic", data_16}, | |
| {58, 2, "CC_SOC_coeff", data_16}, | |
| {60, 2, "nominal_capacity", data_16}, | |
| {62, 2, "actual_capacity", data_16}, | |
| {68, 1, "temperature_counter", data_8}, | |
| {69, 3, "Vbatt_filtered", data_24}, | |
| {72, 3, "Ibatt_filtered", data_24}, | |
| {75, 2, "Current_CC_shadow", data_16}, | |
| {79, 2, "Ibatt_standby", data_16}, | |
| {82, 1, "Auto_recharge_SOC_threshold", data_8}, | |
| {83, 2, "System_cutoff_voltage", data_16}, | |
| {85, 2, "System_CC_to_CV_voltage", data_16}, | |
| {87, 2, "System_term_current", data_16}, | |
| {89, 2, "System_fake_term_current", data_16}, | |
| {91, 2, "thermistor_c1_coeff", data_16}, | |
| }; | |
| static struct fg_regs fg_cfg[] = { | |
| {0, 2, "ESR_actual", data_16}, | |
| {4, 1, "IRQ_SOC_max", data_8}, | |
| {5, 1, "IRQ_SOC_min", data_8}, | |
| {6, 1, "IRQ_volt_empty", data_8}, | |
| {7, 1, "Temp_external", data_8}, | |
| {8, 1, "IRQ_delta_threshold", data_8}, | |
| {9, 1, "JIETA_soft_cold", data_8}, | |
| {10, 1, "JIETA_soft_hot", data_8}, | |
| {11, 1, "IRQ_volt_min", data_8}, | |
| {14, 2, "ESR_sys_replace", data_16}, | |
| }; | |
| static struct fg_regs fg_shdw[] = { | |
| {0, 1, "Latest_battery_info", data_8}, | |
| {1, 1, "Latest_Msys_SOC", data_8}, | |
| {2, 2, "Battery_capacity", data_16}, | |
| {4, 2, "Rslow_drop", data_16}, | |
| {6, 1, "Latest_SOC", data_8}, | |
| {7, 1, "Latest_Cutoff_SOC", data_8}, | |
| {8, 1, "Latest_full_SOC", data_8}, | |
| {9, 2, "Voltage_shadow", data_16}, | |
| {11, 2, "Current_shadow", data_16}, | |
| {13, 2, "Latest_temperature", data_16}, | |
| {15, 1, "Latest_system_sbits", data_8}, | |
| }; | |
| #define FIRST_FG_CFG_REG 0x20 | |
| #define LAST_FG_CFG_REG 0x2F | |
| #define FIRST_FG_SHDW_REG 0x60 | |
| #define LAST_FG_SHDW_REG 0x6F | |
| #define FG_SCRATCH_PAD_MAX 93 | |
| #define FG_SCRATCH_PAD_BASE_REG 0x80 | |
| #define SMB1360_I2C_READ_LENGTH 32 | |
| static int smb1360_check_cycle_stretch(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| u8 reg; | |
| rc = smb1360_read(chip, STATUS_4_REG, ®); | |
| if (rc) { | |
| pr_err("Unable to read status regiseter\n"); | |
| } else if (reg & CYCLE_STRETCH_ACTIVE_BIT) { | |
| /* clear cycle stretch */ | |
| rc = smb1360_masked_write(chip, CMD_I2C_REG, | |
| CYCLE_STRETCH_CLEAR_BIT, CYCLE_STRETCH_CLEAR_BIT); | |
| if (rc) | |
| pr_err("Unable to clear cycle stretch\n"); | |
| } | |
| return rc; | |
| } | |
| static int show_fg_regs(struct seq_file *m, void *data) | |
| { | |
| struct smb1360_chip *chip = m->private; | |
| int rc, i , j, rem_length; | |
| u8 reg[FG_SCRATCH_PAD_MAX]; | |
| rc = smb1360_check_cycle_stretch(chip); | |
| if (rc) | |
| pr_err("Unable to check cycle-stretch\n"); | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't request FG access rc=%d\n", rc); | |
| return rc; | |
| } | |
| for (i = 0; i < (FG_SCRATCH_PAD_MAX / SMB1360_I2C_READ_LENGTH); i++) { | |
| j = i * SMB1360_I2C_READ_LENGTH; | |
| rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j, | |
| ®[j], SMB1360_I2C_READ_LENGTH); | |
| if (rc) { | |
| pr_err("Couldn't read scratch registers rc=%d\n", rc); | |
| break; | |
| } | |
| } | |
| j = i * SMB1360_I2C_READ_LENGTH; | |
| rem_length = (FG_SCRATCH_PAD_MAX % SMB1360_I2C_READ_LENGTH); | |
| if (rem_length) { | |
| rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j, | |
| ®[j], rem_length); | |
| if (rc) | |
| pr_err("Couldn't read scratch registers rc=%d\n", rc); | |
| } | |
| rc = smb1360_disable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't disable FG access rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_check_cycle_stretch(chip); | |
| if (rc) | |
| pr_err("Unable to check cycle-stretch\n"); | |
| seq_puts(m, "FG scratch-pad registers\n"); | |
| for (i = 0; i < ARRAY_SIZE(fg_scratch_pad); i++) | |
| seq_printf(m, "\t%s = %x\n", fg_scratch_pad[i].param_name, | |
| fg_scratch_pad[i].calc_func(®[fg_scratch_pad[i].index])); | |
| rem_length = LAST_FG_CFG_REG - FIRST_FG_CFG_REG + 1; | |
| rc = smb1360_read_bytes(chip, FIRST_FG_CFG_REG, | |
| ®[0], rem_length); | |
| if (rc) | |
| pr_err("Couldn't read config registers rc=%d\n", rc); | |
| seq_puts(m, "FG config registers\n"); | |
| for (i = 0; i < ARRAY_SIZE(fg_cfg); i++) | |
| seq_printf(m, "\t%s = %x\n", fg_cfg[i].param_name, | |
| fg_cfg[i].calc_func(®[fg_cfg[i].index])); | |
| rem_length = LAST_FG_SHDW_REG - FIRST_FG_SHDW_REG + 1; | |
| rc = smb1360_read_bytes(chip, FIRST_FG_SHDW_REG, | |
| ®[0], rem_length); | |
| if (rc) | |
| pr_err("Couldn't read shadow registers rc=%d\n", rc); | |
| seq_puts(m, "FG shadow registers\n"); | |
| for (i = 0; i < ARRAY_SIZE(fg_shdw); i++) | |
| seq_printf(m, "\t%s = %x\n", fg_shdw[i].param_name, | |
| fg_shdw[i].calc_func(®[fg_shdw[i].index])); | |
| return rc; | |
| } | |
| static int fg_regs_open(struct inode *inode, struct file *file) | |
| { | |
| struct smb1360_chip *chip = inode->i_private; | |
| return single_open(file, show_fg_regs, chip); | |
| } | |
| static const struct file_operations fg_regs_debugfs_ops = { | |
| .owner = THIS_MODULE, | |
| .open = fg_regs_open, | |
| .read = seq_read, | |
| .llseek = seq_lseek, | |
| .release = single_release, | |
| }; | |
| static int smb1360_otg_regulator_enable(struct regulator_dev *rdev) | |
| { | |
| int rc = 0; | |
| struct smb1360_chip *chip = rdev_get_drvdata(rdev); | |
| rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT, | |
| CMD_OTG_EN_BIT); | |
| if (rc) { | |
| pr_err("Couldn't enable OTG mode rc=%d\n", rc); | |
| return rc; | |
| } | |
| pr_debug("OTG mode enabled\n"); | |
| /* Enable current gain configuration */ | |
| mutex_lock(&chip->otp_gain_lock); | |
| if (chip->otg_fet_present) { | |
| /* Enable FET */ | |
| gpio_set_value(chip->otg_fet_enable_gpio, 0); | |
| rc = smb1360_otp_gain_config(chip, 3); | |
| if (rc < 0) | |
| pr_err("Couldn't config OTP gain config rc=%d\n", rc); | |
| else | |
| chip->fet_gain_enabled = true; | |
| } | |
| mutex_unlock(&chip->otp_gain_lock); | |
| return rc; | |
| } | |
| static int smb1360_otg_regulator_disable(struct regulator_dev *rdev) | |
| { | |
| int rc = 0; | |
| struct smb1360_chip *chip = rdev_get_drvdata(rdev); | |
| rc = smb1360_otg_disable(chip); | |
| if (rc) | |
| pr_err("Couldn't disable OTG regulator rc=%d\n", rc); | |
| pr_debug("OTG mode disabled\n"); | |
| return rc; | |
| } | |
| static int smb1360_otg_regulator_is_enable(struct regulator_dev *rdev) | |
| { | |
| u8 reg = 0; | |
| int rc = 0; | |
| struct smb1360_chip *chip = rdev_get_drvdata(rdev); | |
| rc = smb1360_read(chip, CMD_CHG_REG, ®); | |
| if (rc) { | |
| pr_err("Couldn't read OTG enable bit rc=%d\n", rc); | |
| return rc; | |
| } | |
| return (reg & CMD_OTG_EN_BIT) ? 1 : 0; | |
| } | |
| struct regulator_ops smb1360_otg_reg_ops = { | |
| .enable = smb1360_otg_regulator_enable, | |
| .disable = smb1360_otg_regulator_disable, | |
| .is_enabled = smb1360_otg_regulator_is_enable, | |
| }; | |
| static int smb1360_regulator_init(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| struct regulator_init_data *init_data; | |
| struct regulator_config cfg = {}; | |
| init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node); | |
| if (!init_data) { | |
| dev_err(chip->dev, "Unable to allocate memory\n"); | |
| return -ENOMEM; | |
| } | |
| if (init_data->constraints.name) { | |
| chip->otg_vreg.rdesc.owner = THIS_MODULE; | |
| chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE; | |
| chip->otg_vreg.rdesc.ops = &smb1360_otg_reg_ops; | |
| chip->otg_vreg.rdesc.name = init_data->constraints.name; | |
| cfg.dev = chip->dev; | |
| cfg.init_data = init_data; | |
| cfg.driver_data = chip; | |
| cfg.of_node = chip->dev->of_node; | |
| init_data->constraints.valid_ops_mask | |
| |= REGULATOR_CHANGE_STATUS; | |
| chip->otg_vreg.rdev = regulator_register( | |
| &chip->otg_vreg.rdesc, &cfg); | |
| if (IS_ERR(chip->otg_vreg.rdev)) { | |
| rc = PTR_ERR(chip->otg_vreg.rdev); | |
| chip->otg_vreg.rdev = NULL; | |
| if (rc != -EPROBE_DEFER) | |
| dev_err(chip->dev, | |
| "OTG reg failed, rc=%d\n", rc); | |
| } | |
| } | |
| return rc; | |
| } | |
| static int smb1360_check_batt_profile(struct smb1360_chip *chip) | |
| { | |
| int rc, i, timeout = 50; | |
| u8 reg = 0, loaded_profile, new_profile = 0, bid_mask; | |
| if (!chip->connected_rid) { | |
| pr_debug("Skip batt-profile loading connected_rid=%d\n", | |
| chip->connected_rid); | |
| return 0; | |
| } | |
| rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®); | |
| if (rc) { | |
| pr_err("Couldn't read FG_BATT_STATUS rc=%d\n", rc); | |
| return rc; | |
| } | |
| loaded_profile = !!(reg & BATTERY_PROFILE_BIT) ? | |
| BATTERY_PROFILE_B : BATTERY_PROFILE_A; | |
| pr_debug("fg_batt_status=%x loaded_profile=%d\n", reg, loaded_profile); | |
| for (i = 0; i < BATTERY_PROFILE_MAX; i++) { | |
| pr_debug("profile=%d profile_rid=%d connected_rid=%d\n", i, | |
| chip->profile_rid[i], | |
| chip->connected_rid); | |
| if (abs(chip->profile_rid[i] - chip->connected_rid) < | |
| (div_u64(chip->connected_rid, 10))) | |
| break; | |
| } | |
| if (i == BATTERY_PROFILE_MAX) { | |
| pr_err("None of the battery-profiles match the connected-RID\n"); | |
| return 0; | |
| } else { | |
| if (i == loaded_profile) { | |
| pr_debug("Loaded Profile-RID == connected-RID\n"); | |
| return 0; | |
| } else { | |
| new_profile = (loaded_profile == BATTERY_PROFILE_A) ? | |
| BATTERY_PROFILE_B : BATTERY_PROFILE_A; | |
| bid_mask = (new_profile == BATTERY_PROFILE_A) ? | |
| BATT_PROFILEA_MASK : BATT_PROFILEB_MASK; | |
| pr_info("Loaded Profile-RID != connected-RID, switch-profile old_profile=%d new_profile=%d\n", | |
| loaded_profile, new_profile); | |
| } | |
| } | |
| /* set the BID mask */ | |
| rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG, | |
| BATT_PROFILE_SELECT_MASK, bid_mask); | |
| if (rc) { | |
| pr_err("Couldn't reset battery-profile rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("FG access timed-out, rc = %d\n", rc); | |
| return rc; | |
| } | |
| /* delay after handshaking for profile-switch to continue */ | |
| msleep(1500); | |
| rc = smb1360_force_fg_reset(chip); | |
| if (rc) { | |
| pr_err("Couldn't reset FG rc=%d\n", rc); | |
| goto restore_fg; | |
| } | |
| rc = smb1360_disable_fg_access(chip); | |
| if (rc) { | |
| pr_err("disable FG access failed, rc = %d\n", rc); | |
| return rc; | |
| } | |
| timeout = 10; | |
| while (timeout) { | |
| /* delay for profile to change */ | |
| msleep(500); | |
| rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®); | |
| if (rc) { | |
| pr_err("Could't read FG_BATT_STATUS rc=%d\n", rc); | |
| return rc; | |
| } | |
| reg = !!(reg & BATTERY_PROFILE_BIT); | |
| if (reg == new_profile) { | |
| pr_info("New profile=%d loaded\n", new_profile); | |
| break; | |
| } | |
| timeout--; | |
| } | |
| if (!timeout) { | |
| pr_err("New profile could not be loaded\n"); | |
| return -EBUSY; | |
| } | |
| return 0; | |
| restore_fg: | |
| smb1360_disable_fg_access(chip); | |
| return rc; | |
| } | |
| #define UPDATE_IRQ_STAT(irq_reg, value) \ | |
| handlers[irq_reg - IRQ_A_REG].prev_val = value; | |
| static int determine_initial_status(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| u8 reg = 0; | |
| /* | |
| * It is okay to read the IRQ status as the irq's are | |
| * not registered yet. | |
| */ | |
| chip->batt_present = true; | |
| rc = smb1360_read(chip, IRQ_B_REG, ®); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read IRQ_B_REG rc = %d\n", rc); | |
| return rc; | |
| } | |
| UPDATE_IRQ_STAT(IRQ_B_REG, reg); | |
| if (reg & IRQ_B_BATT_TERMINAL_BIT || reg & IRQ_B_BATT_MISSING_BIT) | |
| chip->batt_present = false; | |
| rc = smb1360_read(chip, IRQ_C_REG, ®); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't read IRQ_C_REG rc = %d\n", rc); | |
| return rc; | |
| } | |
| UPDATE_IRQ_STAT(IRQ_C_REG, reg); | |
| if (reg & IRQ_C_CHG_TERM) | |
| chip->batt_full = true; | |
| rc = smb1360_read(chip, IRQ_A_REG, ®); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc); | |
| return rc; | |
| } | |
| UPDATE_IRQ_STAT(IRQ_A_REG, reg); | |
| if (chip->workaround_flags & WRKRND_HARD_JEITA) { | |
| schedule_delayed_work(&chip->jeita_work, 0); | |
| } else { | |
| if (reg & IRQ_A_HOT_HARD_BIT) | |
| chip->batt_hot = true; | |
| if (reg & IRQ_A_COLD_HARD_BIT) | |
| chip->batt_cold = true; | |
| if (!chip->config_hard_thresholds) { | |
| if (reg & IRQ_A_HOT_SOFT_BIT) | |
| chip->batt_warm = true; | |
| if (reg & IRQ_A_COLD_SOFT_BIT) | |
| chip->batt_cool = true; | |
| } | |
| } | |
| rc = smb1360_read(chip, IRQ_E_REG, ®); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc); | |
| return rc; | |
| } | |
| UPDATE_IRQ_STAT(IRQ_E_REG, reg); | |
| chip->usb_present = (reg & IRQ_E_USBIN_UV_BIT) ? false : true; | |
| power_supply_set_present(chip->usb_psy, chip->usb_present); | |
| return 0; | |
| } | |
| static int smb1360_fg_config(struct smb1360_chip *chip) | |
| { | |
| int rc = 0, temp, fcc_mah; | |
| u8 reg = 0, reg2[2]; | |
| if (chip->fg_reset_at_pon) { | |
| int v_predicted, v_now; | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't enable FG access rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_read_bytes(chip, VOLTAGE_PREDICTED_REG, reg2, 2); | |
| if (rc) { | |
| pr_err("Failed to read VOLTAGE_PREDICTED rc=%d\n", rc); | |
| goto disable_fg_reset; | |
| } | |
| v_predicted = (reg2[1] << 8) | reg2[0]; | |
| v_predicted = div_u64(v_predicted * 5000, 0x7FFF); | |
| rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg2, 2); | |
| if (rc) { | |
| pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc); | |
| goto disable_fg_reset; | |
| } | |
| v_now = (reg2[1] << 8) | reg2[0]; | |
| v_now = div_u64(v_now * 5000, 0x7FFF); | |
| pr_debug("v_predicted=%d v_now=%d reset_threshold=%d\n", | |
| v_predicted, v_now, chip->fg_reset_threshold_mv); | |
| /* | |
| * Reset FG if the predicted voltage is off wrt | |
| * the real-time voltage. | |
| */ | |
| temp = abs(v_predicted - v_now); | |
| if (temp >= chip->fg_reset_threshold_mv) { | |
| pr_info("Reseting FG - v_delta=%d threshold=%d\n", | |
| temp, chip->fg_reset_threshold_mv); | |
| /* delay for the FG access to settle */ | |
| msleep(1500); | |
| rc = smb1360_force_fg_reset(chip); | |
| if (rc) { | |
| pr_err("Couldn't reset FG rc=%d\n", rc); | |
| goto disable_fg_reset; | |
| } | |
| } | |
| disable_fg_reset: | |
| smb1360_disable_fg_access(chip); | |
| } | |
| /* | |
| * The below IRQ thresholds are not accessible in REV_1 | |
| * of SMB1360. | |
| */ | |
| if (!(chip->workaround_flags & WRKRND_FG_CONFIG_FAIL)) { | |
| if (chip->delta_soc != -EINVAL) { | |
| reg = abs(((chip->delta_soc * MAX_8_BITS) / 100) - 1); | |
| pr_debug("delta_soc=%d reg=%x\n", chip->delta_soc, reg); | |
| rc = smb1360_write(chip, SOC_DELTA_REG, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to SOC_DELTA_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| if (chip->soc_min != -EINVAL) { | |
| if (is_between(chip->soc_min, 0, 100)) { | |
| reg = DIV_ROUND_UP(chip->soc_min * MAX_8_BITS, | |
| 100); | |
| pr_debug("soc_min=%d reg=%x\n", | |
| chip->soc_min, reg); | |
| rc = smb1360_write(chip, SOC_MIN_REG, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to SOC_MIN_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| if (chip->soc_max != -EINVAL) { | |
| if (is_between(chip->soc_max, 0, 100)) { | |
| reg = DIV_ROUND_UP(chip->soc_max * MAX_8_BITS, | |
| 100); | |
| pr_debug("soc_max=%d reg=%x\n", | |
| chip->soc_max, reg); | |
| rc = smb1360_write(chip, SOC_MAX_REG, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to SOC_MAX_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| if (chip->voltage_min_mv != -EINVAL) { | |
| temp = (chip->voltage_min_mv - 2500) * MAX_8_BITS; | |
| reg = DIV_ROUND_UP(temp, 2500); | |
| pr_debug("voltage_min=%d reg=%x\n", | |
| chip->voltage_min_mv, reg); | |
| rc = smb1360_write(chip, VTG_MIN_REG, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to VTG_MIN_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| if (chip->voltage_empty_mv != -EINVAL) { | |
| temp = (chip->voltage_empty_mv - 2500) * MAX_8_BITS; | |
| reg = DIV_ROUND_UP(temp, 2500); | |
| pr_debug("voltage_empty=%d reg=%x\n", | |
| chip->voltage_empty_mv, reg); | |
| rc = smb1360_write(chip, VTG_EMPTY_REG, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to VTG_EMPTY_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| /* scratch-pad register config */ | |
| if (chip->batt_capacity_mah != -EINVAL | |
| || chip->v_cutoff_mv != -EINVAL | |
| || chip->fg_iterm_ma != -EINVAL | |
| || chip->fg_ibatt_standby_ma != -EINVAL | |
| || chip->fg_thermistor_c1_coeff != -EINVAL | |
| || chip->fg_cc_to_cv_mv != -EINVAL | |
| || chip->fg_auto_recharge_soc != -EINVAL) { | |
| rc = smb1360_enable_fg_access(chip); | |
| if (rc) { | |
| pr_err("Couldn't enable FG access rc=%d\n", rc); | |
| return rc; | |
| } | |
| /* Update battery capacity */ | |
| if (chip->batt_capacity_mah != -EINVAL) { | |
| rc = smb1360_read_bytes(chip, ACTUAL_CAPACITY_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Failed to read ACTUAL CAPACITY rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| fcc_mah = (reg2[1] << 8) | reg2[0]; | |
| if (fcc_mah == chip->batt_capacity_mah) { | |
| pr_debug("battery capacity correct\n"); | |
| } else { | |
| /* Update the battery capacity */ | |
| reg2[1] = | |
| (chip->batt_capacity_mah & 0xFF00) >> 8; | |
| reg2[0] = (chip->batt_capacity_mah & 0xFF); | |
| rc = smb1360_write_bytes(chip, | |
| ACTUAL_CAPACITY_REG, reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write batt-capacity rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| rc = smb1360_write_bytes(chip, | |
| NOMINAL_CAPACITY_REG, reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write batt-capacity rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| /* Update CC to SOC COEFF */ | |
| if (chip->cc_soc_coeff != -EINVAL) { | |
| reg2[1] = | |
| (chip->cc_soc_coeff & 0xFF00) >> 8; | |
| reg2[0] = (chip->cc_soc_coeff & 0xFF); | |
| rc = smb1360_write_bytes(chip, | |
| CC_TO_SOC_COEFF, reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write cc_soc_coeff rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| } | |
| } | |
| } | |
| /* Update cutoff voltage for SOC = 0 */ | |
| if (chip->v_cutoff_mv != -EINVAL) { | |
| temp = (u16) div_u64(chip->v_cutoff_mv * 0x7FFF, 5000); | |
| reg2[1] = (temp & 0xFF00) >> 8; | |
| reg2[0] = temp & 0xFF; | |
| rc = smb1360_write_bytes(chip, FG_SYS_CUTOFF_V_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write cutoff_mv rc=%d\n", rc); | |
| goto disable_fg; | |
| } | |
| } | |
| /* | |
| * Update FG iterm for SOC = 100, this value is always assumed | |
| * to be -ve | |
| */ | |
| if (chip->fg_iterm_ma != -EINVAL) { | |
| int iterm = chip->fg_iterm_ma * -1; | |
| temp = (s16) div_s64(iterm * 0x7FFF, 2500); | |
| reg2[1] = (temp & 0xFF00) >> 8; | |
| reg2[0] = temp & 0xFF; | |
| rc = smb1360_write_bytes(chip, FG_ITERM_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write fg_iterm rc=%d\n", rc); | |
| goto disable_fg; | |
| } | |
| } | |
| /* | |
| * Update FG iterm standby for SOC = 0, this value is always | |
| * assumed to be +ve | |
| */ | |
| if (chip->fg_ibatt_standby_ma != -EINVAL) { | |
| int iterm = chip->fg_ibatt_standby_ma; | |
| temp = (u16) div_u64(iterm * 0x7FFF, 2500); | |
| reg2[1] = (temp & 0xFF00) >> 8; | |
| reg2[0] = temp & 0xFF; | |
| rc = smb1360_write_bytes(chip, FG_IBATT_STANDBY_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write fg_iterm rc=%d\n", rc); | |
| goto disable_fg; | |
| } | |
| } | |
| /* Update CC_to_CV voltage threshold */ | |
| if (chip->fg_cc_to_cv_mv != -EINVAL) { | |
| temp = (u16) div_u64(chip->fg_cc_to_cv_mv * 0x7FFF, | |
| 5000); | |
| reg2[1] = (temp & 0xFF00) >> 8; | |
| reg2[0] = temp & 0xFF; | |
| rc = smb1360_write_bytes(chip, FG_CC_TO_CV_V_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write cc_to_cv_mv rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| } | |
| /* Update the thermistor c1 coefficient */ | |
| if (chip->fg_thermistor_c1_coeff != -EINVAL) { | |
| reg2[1] = (chip->fg_thermistor_c1_coeff & 0xFF00) >> 8; | |
| reg2[0] = (chip->fg_thermistor_c1_coeff & 0xFF); | |
| rc = smb1360_write_bytes(chip, FG_THERM_C1_COEFF_REG, | |
| reg2, 2); | |
| if (rc) { | |
| pr_err("Couldn't write thermistor_c1_coeff rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| } | |
| /* Update SoC based resume charging threshold */ | |
| if (chip->fg_auto_recharge_soc != -EINVAL) { | |
| rc = smb1360_masked_write(chip, CFG_CHG_FUNC_CTRL_REG, | |
| CHG_RECHG_THRESH_FG_SRC_BIT, | |
| CHG_RECHG_THRESH_FG_SRC_BIT); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to CFG_CHG_FUNC_CTRL_REG rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| reg = DIV_ROUND_UP(chip->fg_auto_recharge_soc * | |
| MAX_8_BITS, 100); | |
| pr_debug("fg_auto_recharge_soc=%d reg=%x\n", | |
| chip->fg_auto_recharge_soc, reg); | |
| rc = smb1360_write(chip, FG_AUTO_RECHARGE_SOC, reg); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't write to FG_AUTO_RECHARGE_SOC rc=%d\n", | |
| rc); | |
| goto disable_fg; | |
| } | |
| } | |
| disable_fg: | |
| /* disable FG access */ | |
| smb1360_disable_fg_access(chip); | |
| } | |
| return rc; | |
| } | |
| static void smb1360_check_feature_support(struct smb1360_chip *chip) | |
| { | |
| if (is_usb100_broken(chip)) { | |
| pr_debug("USB100 is not supported\n"); | |
| chip->workaround_flags |= WRKRND_USB100_FAIL; | |
| } | |
| /* | |
| * FG Configuration | |
| * | |
| * The REV_1 of the chip does not allow access to | |
| * FG config registers (20-2FH). Set the workaround flag. | |
| * Also, the battery detection does not work when the DCIN is absent, | |
| * add a workaround flag for it. | |
| */ | |
| if (chip->revision == SMB1360_REV_1) { | |
| pr_debug("FG config and Battery detection is not supported\n"); | |
| chip->workaround_flags |= | |
| WRKRND_FG_CONFIG_FAIL | WRKRND_BATT_DET_FAIL; | |
| } | |
| } | |
| static int smb1360_enable(struct smb1360_chip *chip, bool enable) | |
| { | |
| int rc = 0; | |
| u8 val = 0, shdn_cmd_polar; | |
| rc = smb1360_read(chip, SHDN_CTRL_REG, &val); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't read 0x1A reg rc = %d\n", rc); | |
| return rc; | |
| } | |
| /* Ignore if a CMD based shutdown is not enabled */ | |
| if (!(val & SHDN_CMD_USE_BIT)) { | |
| pr_debug("SMB not configured for CMD based shutdown\n"); | |
| return 0; | |
| } | |
| shdn_cmd_polar = !!(val & SHDN_CMD_POLARITY_BIT); | |
| val = (shdn_cmd_polar ^ enable) ? SHDN_CMD_BIT : 0; | |
| pr_debug("enable=%d shdn_polarity=%d value=%d\n", enable, | |
| shdn_cmd_polar, val); | |
| rc = smb1360_masked_write(chip, CMD_IL_REG, SHDN_CMD_BIT, val); | |
| if (rc < 0) | |
| pr_err("Couldn't shutdown smb1360 rc = %d\n", rc); | |
| return rc; | |
| } | |
| static inline int smb1360_poweroff(struct smb1360_chip *chip) | |
| { | |
| pr_debug("power off smb1360\n"); | |
| return smb1360_enable(chip, false); | |
| } | |
| static inline int smb1360_poweron(struct smb1360_chip *chip) | |
| { | |
| pr_debug("power on smb1360\n"); | |
| return smb1360_enable(chip, true); | |
| } | |
| static int smb1360_jeita_init(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| int temp; | |
| if (chip->config_hard_thresholds) { | |
| if (chip->soft_jeita_supported) { | |
| chip->workaround_flags |= WRKRND_HARD_JEITA; | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cool_bat_decidegc, chip->warm_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set jeita threshold\n"); | |
| return rc; | |
| } | |
| } else { | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cold_bat_decidegc, chip->hot_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set jeita threshold\n"); | |
| return rc; | |
| } | |
| } | |
| } else { | |
| if (chip->soft_jeita_supported) { | |
| temp = min(chip->warm_bat_ma, chip->cool_bat_ma); | |
| rc = smb1360_set_jeita_comp_curr(chip, temp); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set comp current\n"); | |
| return rc; | |
| } | |
| temp = (chip->vfloat_mv - chip->warm_bat_mv) / 10; | |
| rc = smb1360_masked_write(chip, CFG_FVC_REG, | |
| FLT_VTG_COMP_MASK, temp); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set VFLT compensation = %d", | |
| rc); | |
| return rc; | |
| } | |
| rc = smb1360_set_soft_jeita_threshold(chip, | |
| chip->cool_bat_decidegc, chip->warm_bat_decidegc); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set jeita threshold\n"); | |
| return rc; | |
| } | |
| rc = smb1360_soft_jeita_comp_enable(chip, true); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't enable jeita\n"); | |
| return rc; | |
| } | |
| } | |
| } | |
| return rc; | |
| } | |
| static int smb1360_otp_gain_init(struct smb1360_chip *chip) | |
| { | |
| int rc = 0, gain_factor; | |
| bool otp_gain_config = false; | |
| if (chip->rsense_10mohm) { | |
| gain_factor = 2; | |
| otp_gain_config = true; | |
| } | |
| mutex_lock(&chip->otp_gain_lock); | |
| if (chip->otg_fet_present) { | |
| /* | |
| * Reset current gain to the default value if OTG | |
| * is not enabled | |
| */ | |
| if (!chip->fet_gain_enabled) { | |
| otp_gain_config = true; | |
| gain_factor = 0; | |
| } | |
| } | |
| if (otp_gain_config) { | |
| rc = smb1360_otp_gain_config(chip, gain_factor); | |
| if (rc < 0) | |
| pr_err("Couldn't config OTP gain rc=%d\n", rc); | |
| } | |
| mutex_unlock(&chip->otp_gain_lock); | |
| return rc; | |
| } | |
| static int smb1360_hw_init(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| int i; | |
| u8 reg, mask; | |
| smb1360_check_feature_support(chip); | |
| rc = smb1360_enable_volatile_writes(chip); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't configure for volatile rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| /* Bring SMB1360 out of shutdown, if it was enabled by default */ | |
| rc = smb1360_poweron(chip); | |
| if (rc < 0) { | |
| pr_err("smb1360 power on failed\n"); | |
| return rc; | |
| } else { | |
| /* | |
| * A 2 seconds delay is mandatory after bringing the chip out | |
| * of shutdown. This guarantees that FG is in a proper state. | |
| */ | |
| schedule_delayed_work(&chip->delayed_init_work, | |
| msecs_to_jiffies(SMB1360_POWERON_DELAY_MS)); | |
| } | |
| /* | |
| * set chg en by cmd register, set chg en by writing bit 1, | |
| * enable auto pre to fast | |
| */ | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CHG_EN_BY_PIN_BIT | |
| | CHG_EN_ACTIVE_LOW_BIT | |
| | PRE_TO_FAST_REQ_CMD_BIT, | |
| 0); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set CFG_CHG_MISC_REG rc=%d\n", rc); | |
| return rc; | |
| } | |
| /* USB/AC pin settings */ | |
| rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG, | |
| AC_INPUT_ICL_PIN_BIT | |
| | AC_INPUT_PIN_HIGH_BIT | |
| | RESET_STATE_USB_500, | |
| AC_INPUT_PIN_HIGH_BIT | |
| | RESET_STATE_USB_500); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set CFG_BATT_CHG_ICL_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| /* AICL enable and set input-uv glitch flt to 20ms*/ | |
| reg = AICL_ENABLED_BIT | INPUT_UV_GLITCH_FLT_20MS_BIT; | |
| rc = smb1360_masked_write(chip, CFG_GLITCH_FLT_REG, reg, reg); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set CFG_GLITCH_FLT_REG rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| /* set the float voltage */ | |
| if (chip->vfloat_mv != -EINVAL) { | |
| rc = smb1360_float_voltage_set(chip, chip->vfloat_mv); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't set float voltage rc = %d\n", rc); | |
| return rc; | |
| } | |
| } | |
| /* set iterm */ | |
| if (chip->iterm_ma != -EINVAL) { | |
| if (chip->iterm_disabled) { | |
| dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n"); | |
| return -EINVAL; | |
| } else { | |
| if (chip->rsense_10mohm) | |
| chip->iterm_ma /= 2; | |
| if (chip->iterm_ma < 25) | |
| reg = CHG_ITERM_25MA; | |
| else if (chip->iterm_ma > 200) | |
| reg = CHG_ITERM_200MA; | |
| else | |
| reg = DIV_ROUND_UP(chip->iterm_ma, 25) - 1; | |
| rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG, | |
| CHG_ITERM_MASK, reg); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set iterm rc = %d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CHG_CURR_TERM_DIS_BIT, 0); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't enable iterm rc = %d\n", rc); | |
| return rc; | |
| } | |
| } | |
| } else if (chip->iterm_disabled) { | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CHG_CURR_TERM_DIS_BIT, | |
| CHG_CURR_TERM_DIS_BIT); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set iterm rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| /* set the safety time voltage */ | |
| if (chip->safety_time != -EINVAL) { | |
| if (chip->safety_time == 0) { | |
| /* safety timer disabled */ | |
| rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG, | |
| SAFETY_TIME_DISABLE_BIT, SAFETY_TIME_DISABLE_BIT); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't disable safety timer rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } else { | |
| for (i = 0; i < ARRAY_SIZE(chg_time); i++) { | |
| if (chip->safety_time <= chg_time[i]) { | |
| reg = i << SAFETY_TIME_MINUTES_SHIFT; | |
| break; | |
| } | |
| } | |
| rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG, | |
| SAFETY_TIME_DISABLE_BIT | SAFETY_TIME_MINUTES_MASK, | |
| reg); | |
| if (rc < 0) { | |
| dev_err(chip->dev, | |
| "Couldn't set safety timer rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| /* configure resume threshold, auto recharge and charge inhibit */ | |
| if (chip->resume_delta_mv != -EINVAL) { | |
| if (chip->recharge_disabled && chip->chg_inhibit_disabled) { | |
| dev_err(chip->dev, "Error: Both recharge_disabled and recharge_mv set\n"); | |
| return -EINVAL; | |
| } else { | |
| rc = smb1360_recharge_threshold_set(chip, | |
| chip->resume_delta_mv); | |
| if (rc) { | |
| dev_err(chip->dev, | |
| "Couldn't set rechg thresh rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CFG_AUTO_RECHG_DIS_BIT, | |
| chip->recharge_disabled ? | |
| CFG_AUTO_RECHG_DIS_BIT : 0); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set rechg-cfg rc = %d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CFG_CHG_INHIBIT_EN_BIT, | |
| chip->chg_inhibit_disabled ? | |
| 0 : CFG_CHG_INHIBIT_EN_BIT); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set chg_inhibit rc = %d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG, | |
| CFG_BAT_OV_ENDS_CHG_CYC, | |
| chip->ov_ends_chg_cycle_disabled ? | |
| 0 : CFG_BAT_OV_ENDS_CHG_CYC); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set bat_ov_ends_charge rc = %d\n" | |
| , rc); | |
| return rc; | |
| } | |
| /* battery missing detection */ | |
| rc = smb1360_masked_write(chip, CFG_BATT_MISSING_REG, | |
| BATT_MISSING_SRC_THERM_BIT, | |
| BATT_MISSING_SRC_THERM_BIT); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set batt_missing config = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| rc = smb1360_jeita_init(chip); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't init jeita, rc = %d\n", rc); | |
| return rc; | |
| } | |
| /* interrupt enabling - active low */ | |
| if (chip->client->irq) { | |
| mask = CHG_STAT_IRQ_ONLY_BIT | |
| | CHG_STAT_ACTIVE_HIGH_BIT | |
| | CHG_STAT_DISABLE_BIT | |
| | CHG_TEMP_CHG_ERR_BLINK_BIT; | |
| if (!chip->pulsed_irq) | |
| reg = CHG_STAT_IRQ_ONLY_BIT; | |
| else | |
| reg = CHG_TEMP_CHG_ERR_BLINK_BIT; | |
| rc = smb1360_masked_write(chip, CFG_STAT_CTRL_REG, mask, reg); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set irq config rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| /* enabling only interesting interrupts */ | |
| rc = smb1360_write(chip, IRQ_CFG_REG, | |
| IRQ_BAT_HOT_COLD_HARD_BIT | |
| | IRQ_BAT_HOT_COLD_SOFT_BIT | |
| | IRQ_INTERNAL_TEMPERATURE_BIT | |
| | IRQ_DCIN_UV_BIT | |
| | IRQ_AICL_DONE_BIT); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set irq1 config rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| rc = smb1360_write(chip, IRQ2_CFG_REG, | |
| IRQ2_SAFETY_TIMER_BIT | |
| | IRQ2_CHG_ERR_BIT | |
| | IRQ2_CHG_PHASE_CHANGE_BIT | |
| | IRQ2_POWER_OK_BIT | |
| | IRQ2_BATT_MISSING_BIT | |
| | IRQ2_VBAT_LOW_BIT); | |
| if (rc) { | |
| dev_err(chip->dev, "Couldn't set irq2 config rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| rc = smb1360_write(chip, IRQ3_CFG_REG, | |
| IRQ3_FG_ACCESS_OK_BIT | |
| | IRQ3_SOC_CHANGE_BIT | |
| | IRQ3_SOC_MIN_BIT | |
| | IRQ3_SOC_MAX_BIT | |
| | IRQ3_SOC_EMPTY_BIT | |
| | IRQ3_SOC_FULL_BIT); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set irq3 enable rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| /* batt-id configuration */ | |
| if (chip->batt_id_disabled) { | |
| mask = BATT_ID_ENABLED_BIT | CHG_BATT_ID_FAIL; | |
| reg = CHG_BATT_ID_FAIL; | |
| rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG, | |
| mask, reg); | |
| if (rc < 0) { | |
| dev_err(chip->dev, "Couldn't set batt_id_reg rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| /* USB OTG current limit configuration */ | |
| if (chip->otg_batt_curr_limit != -EINVAL) { | |
| for (i = 0; i < ARRAY_SIZE(otg_curr_ma); i++) { | |
| if (otg_curr_ma[i] >= chip->otg_batt_curr_limit) | |
| break; | |
| } | |
| if (i == ARRAY_SIZE(otg_curr_ma)) | |
| i = i - 1; | |
| rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG, | |
| OTG_CURRENT_MASK, | |
| i << OTG_CURRENT_SHIFT); | |
| if (rc) | |
| pr_err("Couldn't set OTG current limit, rc = %d\n", rc); | |
| } | |
| rc = smb1360_charging_disable(chip, USER, !!chip->charging_disabled); | |
| if (rc) | |
| dev_err(chip->dev, "Couldn't '%s' charging rc = %d\n", | |
| chip->charging_disabled ? "disable" : "enable", rc); | |
| if (chip->parallel_charging) { | |
| rc = smb1360_parallel_charger_enable(chip, PARALLEL_USER, | |
| !chip->charging_disabled); | |
| if (rc) | |
| dev_err(chip->dev, "Couldn't '%s' parallel-charging rc = %d\n", | |
| chip->charging_disabled ? "disable" : "enable", rc); | |
| } | |
| return rc; | |
| } | |
| static int smb1360_delayed_hw_init(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| pr_debug("delayed hw init start!\n"); | |
| if (chip->otp_hard_jeita_config) { | |
| rc = smb1360_hard_jeita_otp_init(chip); | |
| if (rc) { | |
| pr_err("Unable to change the OTP hard jeita, rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| rc = smb1360_check_batt_profile(chip); | |
| if (rc) { | |
| pr_err("Unable to modify battery profile, rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_otp_gain_init(chip); | |
| if (rc) { | |
| pr_err("Unable to config otp gain, rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_fg_config(chip); | |
| if (rc) { | |
| pr_err("Couldn't configure FG rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_check_cycle_stretch(chip); | |
| if (rc) { | |
| pr_err("Unable to check cycle-stretch\n"); | |
| return rc; | |
| } | |
| pr_debug("delayed hw init complete!\n"); | |
| return rc; | |
| } | |
| static void smb1360_delayed_init_work_fn(struct work_struct *work) | |
| { | |
| int rc = 0; | |
| struct smb1360_chip *chip = container_of(work, struct smb1360_chip, | |
| delayed_init_work.work); | |
| rc = smb1360_delayed_hw_init(chip); | |
| if (!rc) { | |
| /* | |
| * If the delayed hw init successfully, update battery | |
| * power_supply to make sure the correct SoC reported | |
| * timely. | |
| */ | |
| power_supply_changed(&chip->batt_psy); | |
| } else if (rc == -ETIMEDOUT) { | |
| /* | |
| * If the delayed hw init failed causing by waiting for | |
| * FG access timed-out, force a FG reset and queue the | |
| * worker again to retry the initialization. | |
| */ | |
| pr_debug("delayed hw init timed-out, retry!"); | |
| rc = smb1360_force_fg_reset(chip); | |
| if (rc) { | |
| pr_err("couldn't reset FG, rc = %d\n", rc); | |
| return; | |
| } | |
| schedule_delayed_work(&chip->delayed_init_work, 0); | |
| } else { | |
| pr_err("delayed hw init failed, rc=%d\n", rc); | |
| } | |
| } | |
| static int smb_parse_batt_id(struct smb1360_chip *chip) | |
| { | |
| int rc = 0, rpull = 0, vref = 0; | |
| int64_t denom, batt_id_uv; | |
| struct device_node *node = chip->dev->of_node; | |
| struct qpnp_vadc_result result; | |
| chip->vadc_dev = qpnp_get_vadc(chip->dev, "smb1360"); | |
| if (IS_ERR(chip->vadc_dev)) { | |
| rc = PTR_ERR(chip->vadc_dev); | |
| if (rc == -EPROBE_DEFER) | |
| pr_err("vadc not found - defer rc=%d\n", rc); | |
| else | |
| pr_err("vadc property missing, rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = of_property_read_u32(node, "qcom,profile-a-rid-kohm", | |
| &chip->profile_rid[0]); | |
| if (rc < 0) { | |
| pr_err("Couldn't read profile-a-rid-kohm rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = of_property_read_u32(node, "qcom,profile-b-rid-kohm", | |
| &chip->profile_rid[1]); | |
| if (rc < 0) { | |
| pr_err("Couldn't read profile-b-rid-kohm rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = of_property_read_u32(node, "qcom,batt-id-vref-uv", &vref); | |
| if (rc < 0) { | |
| pr_err("Couldn't read batt-id-vref-uv rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = of_property_read_u32(node, "qcom,batt-id-rpullup-kohm", &rpull); | |
| if (rc < 0) { | |
| pr_err("Couldn't read batt-id-rpullup-kohm rc=%d\n", rc); | |
| return rc; | |
| } | |
| /* read battery ID */ | |
| rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result); | |
| if (rc) { | |
| pr_err("error reading batt id channel = %d, rc = %d\n", | |
| LR_MUX2_BAT_ID, rc); | |
| return rc; | |
| } | |
| batt_id_uv = result.physical; | |
| if (batt_id_uv == 0) { | |
| /* vadc not correct or batt id line grounded, report 0 kohms */ | |
| pr_err("batt_id_uv = 0, batt-id grounded using same profile\n"); | |
| return 0; | |
| } | |
| denom = div64_s64(vref * 1000000LL, batt_id_uv) - 1000000LL; | |
| if (denom == 0) { | |
| /* batt id connector might be open, return 0 kohms */ | |
| return 0; | |
| } | |
| chip->connected_rid = div64_s64(rpull * 1000000LL + denom/2, denom); | |
| pr_debug("batt_id_voltage = %lld, connected_rid = %d\n", | |
| batt_id_uv, chip->connected_rid); | |
| return 0; | |
| } | |
| /* | |
| * Note the below: | |
| * 1. if both qcom,soft-jeita-supported and qcom,config-hard-thresholds | |
| * are not defined, SMB continues with default OTP configuration. | |
| * 2. if both are enabled, the hard thresholds are modified. | |
| * 3. if only qcom,config-hard-thresholds is defined, the soft JEITA is disabled | |
| * 4. if only qcom,soft-jeita-supported is defined, the soft JEITA thresholds | |
| * are modified. | |
| */ | |
| static int smb1360_parse_jeita_params(struct smb1360_chip *chip) | |
| { | |
| int rc = 0; | |
| struct device_node *node = chip->dev->of_node; | |
| int temp[2]; | |
| if (of_property_read_bool(node, "qcom,config-hard-thresholds")) { | |
| rc = of_property_read_u32(node, | |
| "qcom,cold-bat-decidegc", &chip->cold_bat_decidegc); | |
| if (rc) { | |
| pr_err("cold_bat_decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, | |
| "qcom,hot-bat-decidegc", &chip->hot_bat_decidegc); | |
| if (rc) { | |
| pr_err("hot_bat_decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| chip->config_hard_thresholds = true; | |
| pr_debug("config_hard_thresholds = %d, cold_bat_decidegc = %d, hot_bat_decidegc = %d\n", | |
| chip->config_hard_thresholds, chip->cold_bat_decidegc, | |
| chip->hot_bat_decidegc); | |
| } else if (of_property_read_bool(node, "qcom,otp-hard-jeita-config")) { | |
| rc = of_property_read_u32(node, "qcom,otp-cold-bat-decidegc", | |
| &chip->otp_cold_bat_decidegc); | |
| if (rc) { | |
| pr_err("otp-cold-bat-decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,otp-hot-bat-decidegc", | |
| &chip->otp_hot_bat_decidegc); | |
| if (rc) { | |
| pr_err("otp-hot-bat-decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| chip->otp_hard_jeita_config = true; | |
| rc = of_property_read_u32_array(node, | |
| "qcom,otp-hard-jeita-hysteresis", temp, 2); | |
| if (rc) { | |
| if (rc != -EINVAL) { | |
| pr_err("read otp-hard-jeita-hysteresis failed, rc = %d\n", | |
| rc); | |
| return rc; | |
| } | |
| } else { | |
| chip->cold_hysteresis = temp[0]; | |
| chip->hot_hysteresis = temp[1]; | |
| } | |
| pr_debug("otp_hard_jeita_config = %d, otp_cold_bat_decidegc = %d\n" | |
| "otp_hot_bat_decidegc = %d, cold_hysteresis = %d\n" | |
| "hot_hysteresis = %d\n", | |
| chip->otp_hard_jeita_config, | |
| chip->otp_cold_bat_decidegc, | |
| chip->otp_hot_bat_decidegc, chip->cold_hysteresis, | |
| chip->hot_hysteresis); | |
| } | |
| if (of_property_read_bool(node, "qcom,soft-jeita-supported")) { | |
| rc = of_property_read_u32(node, "qcom,warm-bat-decidegc", | |
| &chip->warm_bat_decidegc); | |
| if (rc) { | |
| pr_err("warm_bat_decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,cool-bat-decidegc", | |
| &chip->cool_bat_decidegc); | |
| if (rc) { | |
| pr_err("cool_bat_decidegc property error, rc = %d\n", | |
| rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,cool-bat-mv", | |
| &chip->cool_bat_mv); | |
| if (rc) { | |
| pr_err("cool_bat_mv property error, rc = %d\n", rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,warm-bat-mv", | |
| &chip->warm_bat_mv); | |
| if (rc) { | |
| pr_err("warm_bat_mv property error, rc = %d\n", rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,cool-bat-ma", | |
| &chip->cool_bat_ma); | |
| if (rc) { | |
| pr_err("cool_bat_ma property error, rc = %d\n", rc); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,warm-bat-ma", | |
| &chip->warm_bat_ma); | |
| if (rc) { | |
| pr_err("warm_bat_ma property error, rc = %d\n", rc); | |
| return -EINVAL; | |
| } | |
| chip->soft_jeita_supported = true; | |
| } else { | |
| /* | |
| * If no soft JEITA configuration required from devicetree, | |
| * read the default soft JEITA setting for hard JEITA | |
| * configuration sanity check. | |
| */ | |
| rc = smb1360_get_soft_jeita_threshold(chip, | |
| &chip->cool_bat_decidegc, | |
| &chip->warm_bat_decidegc); | |
| if (rc) { | |
| pr_err("get default soft JEITA threshold failed, rc=%d\n", | |
| rc); | |
| return rc; | |
| } | |
| } | |
| pr_debug("soft-jeita-enabled = %d, warm-bat-decidegc = %d, cool-bat-decidegc = %d, cool-bat-mv = %d, warm-bat-mv = %d, cool-bat-ma = %d, warm-bat-ma = %d\n", | |
| chip->soft_jeita_supported, chip->warm_bat_decidegc, | |
| chip->cool_bat_decidegc, chip->cool_bat_mv, chip->warm_bat_mv, | |
| chip->cool_bat_ma, chip->warm_bat_ma); | |
| return rc; | |
| } | |
| #define MAX_PARALLEL_CURRENT 540 | |
| static int smb1360_parse_parallel_charging_params(struct smb1360_chip *chip) | |
| { | |
| struct device_node *node = chip->dev->of_node; | |
| if (of_property_read_bool(node, "qcom,parallel-charging-enabled")) { | |
| if (!chip->rsense_10mohm) { | |
| pr_err("10mohm-rsense configuration not enabled - parallel-charging disabled\n"); | |
| return 0; | |
| } | |
| chip->parallel_charging = true; | |
| chip->max_parallel_chg_current = MAX_PARALLEL_CURRENT; | |
| of_property_read_u32(node, "qcom,max-parallel-current-ma", | |
| &chip->max_parallel_chg_current); | |
| pr_debug("Max parallel charger current = %dma\n", | |
| chip->max_parallel_chg_current); | |
| /* mark the parallel-charger as disabled */ | |
| chip->parallel_chg_disable_status |= PARALLEL_CURRENT; | |
| } | |
| return 0; | |
| } | |
| static int smb_parse_dt(struct smb1360_chip *chip) | |
| { | |
| int rc; | |
| struct device_node *node = chip->dev->of_node; | |
| if (!node) { | |
| dev_err(chip->dev, "device tree info. missing\n"); | |
| return -EINVAL; | |
| } | |
| chip->rsense_10mohm = of_property_read_bool(node, "qcom,rsense-10mhom"); | |
| if (of_property_read_bool(node, "qcom,batt-profile-select")) { | |
| rc = smb_parse_batt_id(chip); | |
| if (rc < 0) { | |
| if (rc != -EPROBE_DEFER) | |
| pr_err("Unable to parse batt-id rc=%d\n", rc); | |
| return rc; | |
| } | |
| } | |
| chip->otg_fet_present = of_property_read_bool(node, | |
| "qcom,otg-fet-present"); | |
| if (chip->otg_fet_present) { | |
| chip->otg_fet_enable_gpio = of_get_named_gpio(node, | |
| "qcom,otg-fet-enable-gpio", 0); | |
| if (!gpio_is_valid(chip->otg_fet_enable_gpio)) { | |
| if (chip->otg_fet_enable_gpio != -EPROBE_DEFER) | |
| pr_err("Unable to get OTG FET enable gpio=%d\n", | |
| chip->otg_fet_enable_gpio); | |
| return chip->otg_fet_enable_gpio; | |
| } else { | |
| /* Configure OTG FET control gpio */ | |
| rc = devm_gpio_request_one(chip->dev, | |
| chip->otg_fet_enable_gpio, | |
| GPIOF_OPEN_DRAIN | GPIOF_INIT_HIGH, | |
| "smb1360_otg_fet_gpio"); | |
| if (rc) { | |
| pr_err("Unable to request gpio rc=%d\n", rc); | |
| return rc; | |
| } | |
| } | |
| } | |
| chip->pulsed_irq = of_property_read_bool(node, "qcom,stat-pulsed-irq"); | |
| rc = of_property_read_u32(node, "qcom,float-voltage-mv", | |
| &chip->vfloat_mv); | |
| if (rc < 0) | |
| chip->vfloat_mv = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,charging-timeout", | |
| &chip->safety_time); | |
| if (rc < 0) | |
| chip->safety_time = -EINVAL; | |
| if (!rc && (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) { | |
| dev_err(chip->dev, "Bad charging-timeout %d\n", | |
| chip->safety_time); | |
| return -EINVAL; | |
| } | |
| rc = of_property_read_u32(node, "qcom,recharge-thresh-mv", | |
| &chip->resume_delta_mv); | |
| if (rc < 0) | |
| chip->resume_delta_mv = -EINVAL; | |
| chip->recharge_disabled = of_property_read_bool(node, | |
| "qcom,recharge-disabled"); | |
| rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma); | |
| if (rc < 0) | |
| chip->iterm_ma = -EINVAL; | |
| chip->iterm_disabled = of_property_read_bool(node, | |
| "qcom,iterm-disabled"); | |
| chip->chg_inhibit_disabled = of_property_read_bool(node, | |
| "qcom,chg-inhibit-disabled"); | |
| chip->charging_disabled = of_property_read_bool(node, | |
| "qcom,charging-disabled"); | |
| chip->batt_id_disabled = of_property_read_bool(node, | |
| "qcom,batt-id-disabled"); | |
| chip->shdn_after_pwroff = of_property_read_bool(node, | |
| "qcom,shdn-after-pwroff"); | |
| chip->min_icl_usb100 = of_property_read_bool(node, | |
| "qcom,min-icl-100ma"); | |
| chip->ov_ends_chg_cycle_disabled = of_property_read_bool(node, | |
| "qcom,disable-ov-ends-chg-cycle"); | |
| rc = smb1360_parse_parallel_charging_params(chip); | |
| if (rc) { | |
| pr_err("Couldn't parse parallel charginng params rc=%d\n", rc); | |
| return rc; | |
| } | |
| if (of_find_property(node, "qcom,thermal-mitigation", | |
| &chip->thermal_levels)) { | |
| chip->thermal_mitigation = devm_kzalloc(chip->dev, | |
| chip->thermal_levels, | |
| GFP_KERNEL); | |
| if (chip->thermal_mitigation == NULL) { | |
| pr_err("thermal mitigation kzalloc() failed.\n"); | |
| return -ENOMEM; | |
| } | |
| chip->thermal_levels /= sizeof(int); | |
| rc = of_property_read_u32_array(node, | |
| "qcom,thermal-mitigation", | |
| chip->thermal_mitigation, chip->thermal_levels); | |
| if (rc) { | |
| pr_err("Couldn't read threm limits rc = %d\n", rc); | |
| return rc; | |
| } | |
| } | |
| rc = smb1360_parse_jeita_params(chip); | |
| if (rc < 0) { | |
| pr_err("Couldn't parse jeita params, rc = %d\n", rc); | |
| return rc; | |
| } | |
| /* fg params */ | |
| chip->empty_soc_disabled = of_property_read_bool(node, | |
| "qcom,empty-soc-disabled"); | |
| rc = of_property_read_u32(node, "qcom,fg-delta-soc", &chip->delta_soc); | |
| if (rc < 0) | |
| chip->delta_soc = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-soc-max", &chip->soc_max); | |
| if (rc < 0) | |
| chip->soc_max = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-soc-min", &chip->soc_min); | |
| if (rc < 0) | |
| chip->soc_min = -EINVAL; | |
| chip->awake_min_soc = of_property_read_bool(node, | |
| "qcom,awake-min-soc"); | |
| rc = of_property_read_u32(node, "qcom,fg-voltage-min-mv", | |
| &chip->voltage_min_mv); | |
| if (rc < 0) | |
| chip->voltage_min_mv = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-voltage-empty-mv", | |
| &chip->voltage_empty_mv); | |
| if (rc < 0) | |
| chip->voltage_empty_mv = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-batt-capacity-mah", | |
| &chip->batt_capacity_mah); | |
| if (rc < 0) | |
| chip->batt_capacity_mah = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-cc-soc-coeff", | |
| &chip->cc_soc_coeff); | |
| if (rc < 0) | |
| chip->cc_soc_coeff = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage-mv", | |
| &chip->v_cutoff_mv); | |
| if (rc < 0) | |
| chip->v_cutoff_mv = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-iterm-ma", | |
| &chip->fg_iterm_ma); | |
| if (rc < 0) | |
| chip->fg_iterm_ma = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-ibatt-standby-ma", | |
| &chip->fg_ibatt_standby_ma); | |
| if (rc < 0) | |
| chip->fg_ibatt_standby_ma = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,thermistor-c1-coeff", | |
| &chip->fg_thermistor_c1_coeff); | |
| if (rc < 0) | |
| chip->fg_thermistor_c1_coeff = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-cc-to-cv-mv", | |
| &chip->fg_cc_to_cv_mv); | |
| if (rc < 0) | |
| chip->fg_cc_to_cv_mv = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,otg-batt-curr-limit", | |
| &chip->otg_batt_curr_limit); | |
| if (rc < 0) | |
| chip->otg_batt_curr_limit = -EINVAL; | |
| rc = of_property_read_u32(node, "qcom,fg-auto-recharge-soc", | |
| &chip->fg_auto_recharge_soc); | |
| if (rc < 0) | |
| chip->fg_auto_recharge_soc = -EINVAL; | |
| if (of_property_read_bool(node, "qcom,fg-reset-at-pon")) { | |
| chip->fg_reset_at_pon = true; | |
| rc = of_property_read_u32(node, "qcom,fg-reset-thresold-mv", | |
| &chip->fg_reset_threshold_mv); | |
| if (rc) { | |
| pr_debug("FG reset voltage threshold not specified using 50mV\n"); | |
| chip->fg_reset_threshold_mv = FG_RESET_THRESHOLD_MV; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int smb1360_probe(struct i2c_client *client, | |
| const struct i2c_device_id *id) | |
| { | |
| u8 reg; | |
| int rc; | |
| struct smb1360_chip *chip; | |
| struct power_supply *usb_psy; | |
| usb_psy = power_supply_get_by_name("usb"); | |
| if (!usb_psy) { | |
| dev_dbg(&client->dev, "USB supply not found; defer probe\n"); | |
| return -EPROBE_DEFER; | |
| } | |
| chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | |
| if (!chip) { | |
| dev_err(&client->dev, "Unable to allocate memory\n"); | |
| return -ENOMEM; | |
| } | |
| chip->resume_completed = true; | |
| chip->client = client; | |
| chip->dev = &client->dev; | |
| chip->usb_psy = usb_psy; | |
| chip->fake_battery_soc = -EINVAL; | |
| mutex_init(&chip->read_write_lock); | |
| mutex_init(&chip->parallel_chg_lock); | |
| mutex_init(&chip->otp_gain_lock); | |
| mutex_init(&chip->fg_access_request_lock); | |
| INIT_DELAYED_WORK(&chip->jeita_work, smb1360_jeita_work_fn); | |
| INIT_DELAYED_WORK(&chip->delayed_init_work, | |
| smb1360_delayed_init_work_fn); | |
| init_completion(&chip->fg_mem_access_granted); | |
| smb1360_wakeup_src_init(chip); | |
| /* probe the device to check if its actually connected */ | |
| rc = smb1360_read(chip, CFG_BATT_CHG_REG, ®); | |
| if (rc) { | |
| pr_err("Failed to detect SMB1360, device may be absent\n"); | |
| return -ENODEV; | |
| } | |
| rc = read_revision(chip, &chip->revision); | |
| if (rc) | |
| dev_err(chip->dev, "Couldn't read revision rc = %d\n", rc); | |
| rc = smb_parse_dt(chip); | |
| if (rc < 0) { | |
| dev_err(&client->dev, "Unable to parse DT nodes\n"); | |
| return rc; | |
| } | |
| device_init_wakeup(chip->dev, 1); | |
| i2c_set_clientdata(client, chip); | |
| mutex_init(&chip->irq_complete); | |
| mutex_init(&chip->charging_disable_lock); | |
| mutex_init(&chip->current_change_lock); | |
| chip->default_i2c_addr = client->addr; | |
| INIT_WORK(&chip->parallel_work, smb1360_parallel_work); | |
| if (chip->cold_hysteresis || chip->hot_hysteresis) | |
| INIT_WORK(&chip->jeita_hysteresis_work, | |
| smb1360_jeita_hysteresis_work); | |
| pr_debug("default_i2c_addr=%x\n", chip->default_i2c_addr); | |
| smb1360_otp_backup_pool_init(chip); | |
| rc = smb1360_hw_init(chip); | |
| if (rc < 0) { | |
| dev_err(&client->dev, | |
| "Unable to intialize hardware rc = %d\n", rc); | |
| return rc; | |
| } | |
| rc = smb1360_regulator_init(chip); | |
| if (rc) { | |
| dev_err(&client->dev, | |
| "Couldn't initialize smb349 ragulator rc=%d\n", rc); | |
| return rc; | |
| } | |
| rc = determine_initial_status(chip); | |
| if (rc < 0) { | |
| dev_err(&client->dev, | |
| "Unable to determine init status rc = %d\n", rc); | |
| goto fail_hw_init; | |
| } | |
| chip->batt_psy.name = "battery"; | |
| chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY; | |
| chip->batt_psy.get_property = smb1360_battery_get_property; | |
| chip->batt_psy.set_property = smb1360_battery_set_property; | |
| chip->batt_psy.properties = smb1360_battery_properties; | |
| chip->batt_psy.num_properties = ARRAY_SIZE(smb1360_battery_properties); | |
| chip->batt_psy.external_power_changed = smb1360_external_power_changed; | |
| chip->batt_psy.property_is_writeable = smb1360_battery_is_writeable; | |
| rc = power_supply_register(chip->dev, &chip->batt_psy); | |
| if (rc < 0) { | |
| dev_err(&client->dev, | |
| "Unable to register batt_psy rc = %d\n", rc); | |
| goto fail_hw_init; | |
| } | |
| /* STAT irq configuration */ | |
| if (client->irq) { | |
| rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, | |
| smb1360_stat_handler, IRQF_ONESHOT, | |
| "smb1360_stat_irq", chip); | |
| if (rc < 0) { | |
| dev_err(&client->dev, | |
| "request_irq for irq=%d failed rc = %d\n", | |
| client->irq, rc); | |
| goto unregister_batt_psy; | |
| } | |
| enable_irq_wake(client->irq); | |
| } | |
| chip->debug_root = debugfs_create_dir("smb1360", NULL); | |
| if (!chip->debug_root) | |
| dev_err(chip->dev, "Couldn't create debug dir\n"); | |
| if (chip->debug_root) { | |
| struct dentry *ent; | |
| ent = debugfs_create_file("config_registers", S_IFREG | S_IRUGO, | |
| chip->debug_root, chip, | |
| &cnfg_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create cnfg debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("status_registers", S_IFREG | S_IRUGO, | |
| chip->debug_root, chip, | |
| &status_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create status debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("irq_status", S_IFREG | S_IRUGO, | |
| chip->debug_root, chip, | |
| &irq_stat_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create irq_stat debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("cmd_registers", S_IFREG | S_IRUGO, | |
| chip->debug_root, chip, | |
| &cmd_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create cmd debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("fg_regs", | |
| S_IFREG | S_IRUGO, chip->debug_root, chip, | |
| &fg_regs_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create fg_scratch_pad debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, | |
| &(chip->peek_poke_address)); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create address debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, chip, | |
| &poke_poke_debug_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create data debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_x32("fg_address", | |
| S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, | |
| &(chip->fg_peek_poke_address)); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create address debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("fg_data", | |
| S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, chip, | |
| &fg_poke_poke_debug_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create data debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_x32("fg_access_type", | |
| S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, | |
| &(chip->fg_access_type)); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create data debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_x32("skip_writes", | |
| S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, | |
| &(chip->skip_writes)); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create data debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_x32("skip_reads", | |
| S_IFREG | S_IWUSR | S_IRUGO, | |
| chip->debug_root, | |
| &(chip->skip_reads)); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create data debug file rc = %d\n", | |
| rc); | |
| ent = debugfs_create_file("irq_count", S_IFREG | S_IRUGO, | |
| chip->debug_root, chip, | |
| &irq_count_debugfs_ops); | |
| if (!ent) | |
| dev_err(chip->dev, | |
| "Couldn't create count debug file rc = %d\n", | |
| rc); | |
| } | |
| dev_info(chip->dev, "SMB1360 revision=0x%x probe success! batt=%d usb=%d soc=%d\n", | |
| chip->revision, | |
| smb1360_get_prop_batt_present(chip), | |
| chip->usb_present, | |
| smb1360_get_prop_batt_capacity(chip)); | |
| return 0; | |
| unregister_batt_psy: | |
| power_supply_unregister(&chip->batt_psy); | |
| fail_hw_init: | |
| regulator_unregister(chip->otg_vreg.rdev); | |
| return rc; | |
| } | |
| static int smb1360_remove(struct i2c_client *client) | |
| { | |
| struct smb1360_chip *chip = i2c_get_clientdata(client); | |
| regulator_unregister(chip->otg_vreg.rdev); | |
| power_supply_unregister(&chip->batt_psy); | |
| mutex_destroy(&chip->charging_disable_lock); | |
| mutex_destroy(&chip->current_change_lock); | |
| mutex_destroy(&chip->read_write_lock); | |
| mutex_destroy(&chip->irq_complete); | |
| mutex_destroy(&chip->otp_gain_lock); | |
| mutex_destroy(&chip->fg_access_request_lock); | |
| debugfs_remove_recursive(chip->debug_root); | |
| return 0; | |
| } | |
| static int smb1360_suspend(struct device *dev) | |
| { | |
| int i, rc; | |
| struct i2c_client *client = to_i2c_client(dev); | |
| struct smb1360_chip *chip = i2c_get_clientdata(client); | |
| /* Save the current IRQ config */ | |
| for (i = 0; i < 3; i++) { | |
| rc = smb1360_read(chip, IRQ_CFG_REG + i, | |
| &chip->irq_cfg_mask[i]); | |
| if (rc) | |
| pr_err("Couldn't save irq cfg regs rc=%d\n", rc); | |
| } | |
| /* enable only important IRQs */ | |
| rc = smb1360_write(chip, IRQ_CFG_REG, IRQ_DCIN_UV_BIT | |
| | IRQ_AICL_DONE_BIT | |
| | IRQ_BAT_HOT_COLD_SOFT_BIT | |
| | IRQ_BAT_HOT_COLD_HARD_BIT); | |
| if (rc < 0) | |
| pr_err("Couldn't set irq_cfg rc=%d\n", rc); | |
| rc = smb1360_write(chip, IRQ2_CFG_REG, IRQ2_BATT_MISSING_BIT | |
| | IRQ2_VBAT_LOW_BIT | |
| | IRQ2_POWER_OK_BIT); | |
| if (rc < 0) | |
| pr_err("Couldn't set irq2_cfg rc=%d\n", rc); | |
| rc = smb1360_write(chip, IRQ3_CFG_REG, IRQ3_SOC_FULL_BIT | |
| | IRQ3_SOC_MIN_BIT | |
| | IRQ3_SOC_EMPTY_BIT); | |
| if (rc < 0) | |
| pr_err("Couldn't set irq3_cfg rc=%d\n", rc); | |
| mutex_lock(&chip->irq_complete); | |
| chip->resume_completed = false; | |
| mutex_unlock(&chip->irq_complete); | |
| return 0; | |
| } | |
| static int smb1360_suspend_noirq(struct device *dev) | |
| { | |
| struct i2c_client *client = to_i2c_client(dev); | |
| struct smb1360_chip *chip = i2c_get_clientdata(client); | |
| if (chip->irq_waiting) { | |
| pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); | |
| return -EBUSY; | |
| } | |
| return 0; | |
| } | |
| static int smb1360_resume(struct device *dev) | |
| { | |
| int i, rc; | |
| struct i2c_client *client = to_i2c_client(dev); | |
| struct smb1360_chip *chip = i2c_get_clientdata(client); | |
| /* Restore the IRQ config */ | |
| for (i = 0; i < 3; i++) { | |
| rc = smb1360_write(chip, IRQ_CFG_REG + i, | |
| chip->irq_cfg_mask[i]); | |
| if (rc) | |
| pr_err("Couldn't restore irq cfg regs rc=%d\n", rc); | |
| } | |
| mutex_lock(&chip->irq_complete); | |
| chip->resume_completed = true; | |
| if (chip->irq_waiting) { | |
| chip->irq_disabled = false; | |
| enable_irq(client->irq); | |
| mutex_unlock(&chip->irq_complete); | |
| smb1360_stat_handler(client->irq, chip); | |
| } else { | |
| mutex_unlock(&chip->irq_complete); | |
| } | |
| power_supply_changed(&chip->batt_psy); | |
| return 0; | |
| } | |
| static void smb1360_shutdown(struct i2c_client *client) | |
| { | |
| int rc; | |
| struct smb1360_chip *chip = i2c_get_clientdata(client); | |
| rc = smb1360_otg_disable(chip); | |
| if (rc) | |
| pr_err("Couldn't disable OTG mode rc=%d\n", rc); | |
| if (chip->shdn_after_pwroff) { | |
| rc = smb1360_poweroff(chip); | |
| if (rc) | |
| pr_err("Couldn't shutdown smb1360, rc = %d\n", rc); | |
| pr_info("smb1360 power off\n"); | |
| } | |
| } | |
| static const struct dev_pm_ops smb1360_pm_ops = { | |
| .resume = smb1360_resume, | |
| .suspend_noirq = smb1360_suspend_noirq, | |
| .suspend = smb1360_suspend, | |
| }; | |
| static struct of_device_id smb1360_match_table[] = { | |
| { .compatible = "qcom,smb1360-chg-fg",}, | |
| { }, | |
| }; | |
| static const struct i2c_device_id smb1360_id[] = { | |
| {"smb1360-chg-fg", 0}, | |
| {}, | |
| }; | |
| MODULE_DEVICE_TABLE(i2c, smb1360_id); | |
| static struct i2c_driver smb1360_driver = { | |
| .driver = { | |
| .name = "smb1360-chg-fg", | |
| .owner = THIS_MODULE, | |
| .of_match_table = smb1360_match_table, | |
| .pm = &smb1360_pm_ops, | |
| }, | |
| .probe = smb1360_probe, | |
| .remove = smb1360_remove, | |
| .shutdown = smb1360_shutdown, | |
| .id_table = smb1360_id, | |
| }; | |
| module_i2c_driver(smb1360_driver); | |
| MODULE_DESCRIPTION("SMB1360 Charger and Fuel Gauge"); | |
| MODULE_LICENSE("GPL v2"); | |
| MODULE_ALIAS("i2c:smb1360-chg-fg"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment