| /* 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) "%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/slab.h> |
| #include <linux/power_supply.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/bitops.h> |
| #include <linux/mutex.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/of_regulator.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/pinctrl/consumer.h> |
| |
| #define SMB135X_BITS_PER_REG 8 |
| |
| /* Mask/Bit helpers */ |
| #define _SMB135X_MASK(BITS, POS) \ |
| ((unsigned char)(((1 << (BITS)) - 1) << (POS))) |
| #define SMB135X_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \ |
| _SMB135X_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \ |
| (RIGHT_BIT_POS)) |
| |
| /* Config registers */ |
| #define CFG_3_REG 0x03 |
| #define CHG_ITERM_50MA 0x08 |
| #define CHG_ITERM_100MA 0x10 |
| #define CHG_ITERM_150MA 0x18 |
| #define CHG_ITERM_200MA 0x20 |
| #define CHG_ITERM_250MA 0x28 |
| #define CHG_ITERM_300MA 0x00 |
| #define CHG_ITERM_500MA 0x30 |
| #define CHG_ITERM_600MA 0x38 |
| #define CHG_ITERM_MASK SMB135X_MASK(5, 3) |
| |
| #define CFG_4_REG 0x04 |
| #define CHG_INHIBIT_MASK SMB135X_MASK(7, 6) |
| #define CHG_INHIBIT_50MV_VAL 0x00 |
| #define CHG_INHIBIT_100MV_VAL 0x40 |
| #define CHG_INHIBIT_200MV_VAL 0x80 |
| #define CHG_INHIBIT_300MV_VAL 0xC0 |
| |
| #define CFG_5_REG 0x05 |
| #define RECHARGE_200MV_BIT BIT(2) |
| #define USB_2_3_BIT BIT(5) |
| |
| #define CFG_A_REG 0x0A |
| #define DCIN_INPUT_MASK SMB135X_MASK(4, 0) |
| |
| #define CFG_C_REG 0x0C |
| #define USBIN_INPUT_MASK SMB135X_MASK(4, 0) |
| #define USBIN_ADAPTER_ALLOWANCE_MASK SMB135X_MASK(7, 5) |
| #define ALLOW_5V_ONLY 0x00 |
| #define ALLOW_5V_OR_9V 0x20 |
| #define ALLOW_5V_TO_9V 0x40 |
| #define ALLOW_9V_ONLY 0x60 |
| |
| #define CFG_D_REG 0x0D |
| |
| #define CFG_E_REG 0x0E |
| #define POLARITY_100_500_BIT BIT(2) |
| #define USB_CTRL_BY_PIN_BIT BIT(1) |
| #define HVDCP_5_9_BIT BIT(4) |
| |
| #define CFG_11_REG 0x11 |
| #define PRIORITY_BIT BIT(7) |
| #define AUTO_SRC_DET_EN_BIT BIT(0) |
| |
| #define USBIN_DCIN_CFG_REG 0x12 |
| #define USBIN_SUSPEND_VIA_COMMAND_BIT BIT(6) |
| |
| #define CFG_14_REG 0x14 |
| #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 DISABLE_CURRENT_TERM_BIT BIT(3) |
| #define DISABLE_AUTO_RECHARGE_BIT BIT(2) |
| #define EN_CHG_INHIBIT_BIT BIT(0) |
| |
| #define CFG_16_REG 0x16 |
| #define SAFETY_TIME_EN_BIT BIT(5) |
| #define SAFETY_TIME_EN_SHIFT 5 |
| #define SAFETY_TIME_MINUTES_MASK SMB135X_MASK(3, 2) |
| #define SAFETY_TIME_MINUTES_SHIFT 2 |
| |
| #define CFG_17_REG 0x17 |
| #define CHG_STAT_DISABLE_BIT BIT(0) |
| #define CHG_STAT_ACTIVE_HIGH_BIT BIT(1) |
| #define CHG_STAT_IRQ_ONLY_BIT BIT(4) |
| |
| #define CFG_19_REG 0x19 |
| #define BATT_MISSING_ALGO_BIT BIT(2) |
| #define BATT_MISSING_THERM_BIT BIT(1) |
| |
| #define CFG_1A_REG 0x1A |
| #define HOT_SOFT_VFLOAT_COMP_EN_BIT BIT(3) |
| #define COLD_SOFT_VFLOAT_COMP_EN_BIT BIT(2) |
| #define HOT_SOFT_CURRENT_COMP_EN_BIT BIT(1) |
| #define COLD_SOFT_CURRENT_COMP_EN_BIT BIT(0) |
| |
| #define CFG_1B_REG 0x1B |
| #define COLD_HARD_MASK SMB135X_MASK(7, 6) |
| #define COLD_HARD_SHIFT 6 |
| #define HOT_HARD_MASK SMB135X_MASK(5, 4) |
| #define HOT_HARD_SHIFT 4 |
| #define COLD_SOFT_MASK SMB135X_MASK(3, 2) |
| #define COLD_SOFT_SHIFT 2 |
| #define HOT_SOFT_MASK SMB135X_MASK(1, 0) |
| #define HOT_SOFT_SHIFT 0 |
| |
| #define VFLOAT_REG 0x1E |
| |
| #define VERSION1_REG 0x2A |
| #define VERSION1_MASK SMB135X_MASK(7, 6) |
| #define VERSION1_SHIFT 6 |
| #define VERSION2_REG 0x32 |
| #define VERSION2_MASK SMB135X_MASK(1, 0) |
| #define VERSION3_REG 0x34 |
| |
| /* Irq Config registers */ |
| #define IRQ_CFG_REG 0x07 |
| #define IRQ_BAT_HOT_COLD_HARD_BIT BIT(7) |
| #define IRQ_BAT_HOT_COLD_SOFT_BIT BIT(6) |
| #define IRQ_OTG_OVER_CURRENT_BIT BIT(4) |
| #define IRQ_USBIN_UV_BIT BIT(2) |
| #define IRQ_INTERNAL_TEMPERATURE_BIT BIT(0) |
| |
| #define IRQ2_CFG_REG 0x08 |
| #define IRQ2_SAFETY_TIMER_BIT BIT(7) |
| #define IRQ2_CHG_ERR_BIT BIT(6) |
| #define IRQ2_CHG_PHASE_CHANGE_BIT BIT(4) |
| #define IRQ2_CHG_INHIBIT_BIT BIT(3) |
| #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 0x09 |
| #define IRQ3_RID_DETECT_BIT BIT(4) |
| #define IRQ3_SRC_DETECT_BIT BIT(2) |
| #define IRQ3_DCIN_UV_BIT BIT(0) |
| |
| #define USBIN_OTG_REG 0x0F |
| #define OTG_CNFG_MASK SMB135X_MASK(3, 2) |
| #define OTG_CNFG_PIN_CTRL 0x04 |
| #define OTG_CNFG_COMMAND_CTRL 0x08 |
| #define OTG_CNFG_AUTO_CTRL 0x0C |
| |
| /* Command Registers */ |
| #define CMD_I2C_REG 0x40 |
| #define ALLOW_VOLATILE_BIT BIT(6) |
| |
| #define CMD_INPUT_LIMIT 0x41 |
| #define USB_SHUTDOWN_BIT BIT(6) |
| #define DC_SHUTDOWN_BIT BIT(5) |
| #define USE_REGISTER_FOR_CURRENT BIT(2) |
| #define USB_100_500_AC_MASK SMB135X_MASK(1, 0) |
| #define USB_100_VAL 0x02 |
| #define USB_500_VAL 0x00 |
| #define USB_AC_VAL 0x01 |
| |
| #define CMD_CHG_REG 0x42 |
| #define CMD_CHG_EN BIT(1) |
| #define OTG_EN BIT(0) |
| |
| /* Status registers */ |
| #define STATUS_1_REG 0x47 |
| #define USING_USB_BIT BIT(1) |
| #define USING_DC_BIT BIT(0) |
| |
| #define STATUS_4_REG 0x4A |
| #define BATT_NET_CHG_CURRENT_BIT BIT(7) |
| #define BATT_LESS_THAN_2V BIT(4) |
| #define CHG_HOLD_OFF_BIT BIT(3) |
| #define CHG_TYPE_MASK SMB135X_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_5_REG 0x4B |
| #define CDP_BIT BIT(7) |
| #define DCP_BIT BIT(6) |
| #define OTHER_BIT BIT(5) |
| #define SDP_BIT BIT(4) |
| #define ACA_A_BIT BIT(3) |
| #define ACA_B_BIT BIT(2) |
| #define ACA_C_BIT BIT(1) |
| #define ACA_DOCK_BIT BIT(0) |
| |
| #define STATUS_6_REG 0x4C |
| #define RID_FLOAT_BIT BIT(3) |
| #define RID_A_BIT BIT(2) |
| #define RID_B_BIT BIT(1) |
| #define RID_C_BIT BIT(0) |
| |
| #define STATUS_8_REG 0x4E |
| #define USBIN_9V BIT(5) |
| #define USBIN_UNREG BIT(4) |
| #define USBIN_LV BIT(3) |
| #define DCIN_9V BIT(2) |
| #define DCIN_UNREG BIT(1) |
| #define DCIN_LV BIT(0) |
| |
| #define STATUS_9_REG 0x4F |
| #define REV_MASK SMB135X_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_B_VBAT_LOW_BIT BIT(2) |
| #define IRQ_B_TEMPERATURE_BIT BIT(0) |
| |
| #define IRQ_C_REG 0x52 |
| #define IRQ_C_TERM_BIT BIT(0) |
| #define IRQ_C_FASTCHG_BIT BIT(6) |
| |
| #define IRQ_D_REG 0x53 |
| #define IRQ_D_TIMEOUT_BIT BIT(2) |
| |
| #define IRQ_E_REG 0x54 |
| #define IRQ_E_DC_OV_BIT BIT(6) |
| #define IRQ_E_DC_UV_BIT BIT(4) |
| #define IRQ_E_USB_OV_BIT BIT(2) |
| #define IRQ_E_USB_UV_BIT BIT(0) |
| |
| #define IRQ_F_REG 0x55 |
| #define IRQ_F_POWER_OK_BIT BIT(0) |
| |
| #define IRQ_G_REG 0x56 |
| #define IRQ_G_SRC_DETECT_BIT BIT(6) |
| |
| enum { |
| WRKARND_USB100_BIT = BIT(0), |
| WRKARND_APSD_FAIL = BIT(1), |
| }; |
| |
| enum { |
| REV_1 = 1, /* Rev 1.0 */ |
| REV_1_1 = 2, /* Rev 1.1 */ |
| REV_2 = 3, /* Rev 2 */ |
| REV_2_1 = 5, /* Rev 2.1 */ |
| REV_MAX, |
| }; |
| |
| static char *revision_str[] = { |
| [REV_1] = "rev1", |
| [REV_1_1] = "rev1.1", |
| [REV_2] = "rev2", |
| [REV_2_1] = "rev2.1", |
| }; |
| |
| enum { |
| V_SMB1356, |
| V_SMB1357, |
| V_SMB1358, |
| V_SMB1359, |
| V_MAX, |
| }; |
| |
| static int version_data[] = { |
| [V_SMB1356] = V_SMB1356, |
| [V_SMB1357] = V_SMB1357, |
| [V_SMB1358] = V_SMB1358, |
| [V_SMB1359] = V_SMB1359, |
| }; |
| |
| static char *version_str[] = { |
| [V_SMB1356] = "smb1356", |
| [V_SMB1357] = "smb1357", |
| [V_SMB1358] = "smb1358", |
| [V_SMB1359] = "smb1359", |
| }; |
| |
| enum { |
| USER = BIT(0), |
| THERMAL = BIT(1), |
| CURRENT = BIT(2), |
| }; |
| |
| enum path_type { |
| USB, |
| DC, |
| }; |
| |
| static int chg_time[] = { |
| 192, |
| 384, |
| 768, |
| 1536, |
| }; |
| |
| static char *pm_batt_supplied_to[] = { |
| "bms", |
| }; |
| |
| struct smb135x_regulator { |
| struct regulator_desc rdesc; |
| struct regulator_dev *rdev; |
| }; |
| |
| struct smb135x_chg { |
| struct i2c_client *client; |
| struct device *dev; |
| struct mutex read_write_lock; |
| |
| u8 revision; |
| int version; |
| |
| bool chg_enabled; |
| bool chg_disabled_permanently; |
| |
| bool usb_present; |
| bool dc_present; |
| bool usb_slave_present; |
| bool dc_ov; |
| |
| bool bmd_algo_disabled; |
| bool iterm_disabled; |
| int iterm_ma; |
| int vfloat_mv; |
| int safety_time; |
| int resume_delta_mv; |
| int fake_battery_soc; |
| struct dentry *debug_root; |
| int usb_current_arr_size; |
| int *usb_current_table; |
| int dc_current_arr_size; |
| int *dc_current_table; |
| bool inhibit_disabled; |
| int fastchg_current_arr_size; |
| int *fastchg_current_table; |
| int fastchg_ma; |
| u8 irq_cfg_mask[3]; |
| int otg_oc_count; |
| struct delayed_work reset_otg_oc_count_work; |
| struct mutex otg_oc_count_lock; |
| |
| bool parallel_charger; |
| bool parallel_charger_present; |
| bool bms_controlled_charging; |
| |
| /* psy */ |
| struct power_supply *usb_psy; |
| int usb_psy_ma; |
| int real_usb_psy_ma; |
| struct power_supply batt_psy; |
| struct power_supply dc_psy; |
| struct power_supply parallel_psy; |
| struct power_supply *bms_psy; |
| int dc_psy_type; |
| int dc_psy_ma; |
| const char *bms_psy_name; |
| |
| /* status tracking */ |
| bool chg_done_batt_full; |
| bool batt_present; |
| bool batt_hot; |
| bool batt_cold; |
| bool batt_warm; |
| bool batt_cool; |
| |
| bool resume_completed; |
| bool irq_waiting; |
| u32 usb_suspended; |
| u32 dc_suspended; |
| struct mutex path_suspend_lock; |
| |
| u32 peek_poke_address; |
| struct smb135x_regulator otg_vreg; |
| int skip_writes; |
| int skip_reads; |
| u32 workaround_flags; |
| bool soft_vfloat_comp_disabled; |
| bool soft_current_comp_disabled; |
| struct mutex irq_complete; |
| struct regulator *therm_bias_vreg; |
| struct regulator *usb_pullup_vreg; |
| struct delayed_work wireless_insertion_work; |
| |
| unsigned int thermal_levels; |
| unsigned int therm_lvl_sel; |
| unsigned int *thermal_mitigation; |
| unsigned int gamma_setting_num; |
| unsigned int *gamma_setting; |
| struct mutex current_change_lock; |
| |
| const char *pinctrl_state_name; |
| struct pinctrl *smb_pinctrl; |
| |
| bool apsd_rerun; |
| bool id_line_not_connected; |
| }; |
| |
| #define RETRY_COUNT 5 |
| int retry_sleep_ms[RETRY_COUNT] = { |
| 10, 20, 30, 40, 50 |
| }; |
| |
| static int __smb135x_read(struct smb135x_chg *chip, int reg, |
| u8 *val) |
| { |
| s32 ret; |
| int retry_count = 0; |
| |
| retry: |
| ret = i2c_smbus_read_byte_data(chip->client, reg); |
| if (ret < 0 && retry_count < RETRY_COUNT) { |
| /* sleep for few ms before retrying */ |
| msleep(retry_sleep_ms[retry_count++]); |
| goto retry; |
| } |
| if (ret < 0) { |
| dev_err(chip->dev, |
| "i2c read fail: can't read from %02x: %d\n", reg, ret); |
| return ret; |
| } else { |
| *val = ret; |
| } |
| |
| return 0; |
| } |
| |
| static int __smb135x_write(struct smb135x_chg *chip, int reg, |
| u8 val) |
| { |
| s32 ret; |
| int retry_count = 0; |
| |
| retry: |
| ret = i2c_smbus_write_byte_data(chip->client, reg, val); |
| if (ret < 0 && retry_count < RETRY_COUNT) { |
| /* sleep for few ms before retrying */ |
| msleep(retry_sleep_ms[retry_count++]); |
| goto retry; |
| } |
| 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 smb135x_read(struct smb135x_chg *chip, int reg, |
| u8 *val) |
| { |
| int rc; |
| |
| if (chip->skip_reads) { |
| *val = 0; |
| return 0; |
| } |
| mutex_lock(&chip->read_write_lock); |
| pm_stay_awake(chip->dev); |
| rc = __smb135x_read(chip, reg, val); |
| pm_relax(chip->dev); |
| mutex_unlock(&chip->read_write_lock); |
| |
| return rc; |
| } |
| |
| static int smb135x_write(struct smb135x_chg *chip, int reg, |
| u8 val) |
| { |
| int rc; |
| |
| if (chip->skip_writes) |
| return 0; |
| |
| mutex_lock(&chip->read_write_lock); |
| pm_stay_awake(chip->dev); |
| rc = __smb135x_write(chip, reg, val); |
| pm_relax(chip->dev); |
| mutex_unlock(&chip->read_write_lock); |
| |
| return rc; |
| } |
| |
| static int smb135x_masked_write(struct smb135x_chg *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 = __smb135x_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 = __smb135x_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 read_revision(struct smb135x_chg *chip, u8 *revision) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, STATUS_9_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read status 9 rc = %d\n", rc); |
| return rc; |
| } |
| *revision = (reg & REV_MASK); |
| return 0; |
| } |
| |
| static int read_version1(struct smb135x_chg *chip, u8 *version) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, VERSION1_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read version 1 rc = %d\n", rc); |
| return rc; |
| } |
| *version = (reg & VERSION1_MASK) >> VERSION1_SHIFT; |
| return 0; |
| } |
| |
| static int read_version2(struct smb135x_chg *chip, u8 *version) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, VERSION2_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read version 2 rc = %d\n", rc); |
| return rc; |
| } |
| *version = (reg & VERSION2_MASK); |
| return 0; |
| } |
| |
| static int read_version3(struct smb135x_chg *chip, u8 *version) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, VERSION3_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read version 3 rc = %d\n", rc); |
| return rc; |
| } |
| *version = reg; |
| return 0; |
| } |
| |
| #define TRIM_23_REG 0x23 |
| #define CHECK_USB100_GOOD_BIT BIT(1) |
| static bool is_usb100_broken(struct smb135x_chg *chip) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, TRIM_23_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read status 9 rc = %d\n", rc); |
| return rc; |
| } |
| return !!(reg & CHECK_USB100_GOOD_BIT); |
| } |
| |
| static bool is_usb_slave_present(struct smb135x_chg *chip) |
| { |
| bool usb_slave_present; |
| u8 reg; |
| int rc; |
| |
| if (chip->id_line_not_connected) |
| return false; |
| |
| rc = smb135x_read(chip, STATUS_6_REG, ®); |
| if (rc < 0) { |
| pr_err("Couldn't read stat 6 rc = %d\n", rc); |
| return false; |
| } |
| |
| if ((reg & (RID_FLOAT_BIT | RID_A_BIT | RID_B_BIT | RID_C_BIT)) == 0) |
| usb_slave_present = 1; |
| else |
| usb_slave_present = 0; |
| |
| pr_debug("stat6= 0x%02x slave_present = %d\n", reg, usb_slave_present); |
| return usb_slave_present; |
| } |
| |
| static char *usb_type_str[] = { |
| "ACA_DOCK", /* bit 0 */ |
| "ACA_C", /* bit 1 */ |
| "ACA_B", /* bit 2 */ |
| "ACA_A", /* bit 3 */ |
| "SDP", /* bit 4 */ |
| "OTHER", /* bit 5 */ |
| "DCP", /* bit 6 */ |
| "CDP", /* bit 7 */ |
| "NONE", /* bit 8 error case */ |
| }; |
| |
| /* helper to return the string of USB type */ |
| static char *get_usb_type_name(u8 stat_5) |
| { |
| unsigned long stat = stat_5; |
| |
| return usb_type_str[find_first_bit(&stat, SMB135X_BITS_PER_REG)]; |
| } |
| |
| static enum power_supply_type usb_type_enum[] = { |
| POWER_SUPPLY_TYPE_USB_ACA, /* bit 0 */ |
| POWER_SUPPLY_TYPE_USB_ACA, /* bit 1 */ |
| POWER_SUPPLY_TYPE_USB_ACA, /* bit 2 */ |
| POWER_SUPPLY_TYPE_USB_ACA, /* bit 3 */ |
| POWER_SUPPLY_TYPE_USB, /* bit 4 */ |
| POWER_SUPPLY_TYPE_UNKNOWN, /* bit 5 */ |
| POWER_SUPPLY_TYPE_USB_DCP, /* bit 6 */ |
| POWER_SUPPLY_TYPE_USB_CDP, /* bit 7 */ |
| POWER_SUPPLY_TYPE_UNKNOWN, /* bit 8 error case, report UNKNWON */ |
| }; |
| |
| /* helper to return enum power_supply_type of USB type */ |
| static enum power_supply_type get_usb_supply_type(u8 stat_5) |
| { |
| unsigned long stat = stat_5; |
| |
| return usb_type_enum[find_first_bit(&stat, SMB135X_BITS_PER_REG)]; |
| } |
| |
| static enum power_supply_property smb135x_battery_properties[] = { |
| 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_HEALTH, |
| POWER_SUPPLY_PROP_TECHNOLOGY, |
| POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, |
| }; |
| |
| static int smb135x_get_prop_batt_status(struct smb135x_chg *chip) |
| { |
| int rc; |
| int status = POWER_SUPPLY_STATUS_DISCHARGING; |
| u8 reg = 0; |
| u8 chg_type; |
| |
| if (chip->chg_done_batt_full) |
| return POWER_SUPPLY_STATUS_FULL; |
| |
| rc = smb135x_read(chip, STATUS_4_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Unable to read STATUS_4_REG rc = %d\n", rc); |
| return POWER_SUPPLY_STATUS_UNKNOWN; |
| } |
| |
| if (reg & CHG_HOLD_OFF_BIT) { |
| /* |
| * when chg hold off happens the battery is |
| * not charging |
| */ |
| status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
| goto out; |
| } |
| |
| chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; |
| |
| if (chg_type == BATT_NOT_CHG_VAL) |
| status = POWER_SUPPLY_STATUS_DISCHARGING; |
| else |
| status = POWER_SUPPLY_STATUS_CHARGING; |
| out: |
| pr_debug("STATUS_4_REG=%x\n", reg); |
| return status; |
| } |
| |
| static int smb135x_get_prop_batt_present(struct smb135x_chg *chip) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = smb135x_read(chip, STATUS_4_REG, ®); |
| if (rc < 0) |
| return 0; |
| |
| /* treat battery gone if less than 2V */ |
| if (reg & BATT_LESS_THAN_2V) |
| return 0; |
| |
| return chip->batt_present; |
| } |
| |
| static int smb135x_get_prop_charge_type(struct smb135x_chg *chip) |
| { |
| int rc; |
| u8 reg; |
| u8 chg_type; |
| |
| rc = smb135x_read(chip, STATUS_4_REG, ®); |
| if (rc < 0) |
| 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) |
| return POWER_SUPPLY_CHARGE_TYPE_FAST; |
| else if (chg_type == BATT_PRE_CHG_VAL) |
| return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
| else if (chg_type == BATT_TAPER_CHG_VAL) |
| return POWER_SUPPLY_CHARGE_TYPE_TAPER; |
| |
| return POWER_SUPPLY_CHARGE_TYPE_NONE; |
| } |
| |
| #define DEFAULT_BATT_CAPACITY 50 |
| static int smb135x_get_prop_batt_capacity(struct smb135x_chg *chip) |
| { |
| union power_supply_propval ret = {0, }; |
| |
| if (chip->fake_battery_soc >= 0) |
| return chip->fake_battery_soc; |
| if (chip->bms_psy) { |
| chip->bms_psy->get_property(chip->bms_psy, |
| POWER_SUPPLY_PROP_CAPACITY, &ret); |
| return ret.intval; |
| } |
| |
| return DEFAULT_BATT_CAPACITY; |
| } |
| |
| static int smb135x_get_prop_batt_health(struct smb135x_chg *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 smb135x_enable_volatile_writes(struct smb135x_chg *chip) |
| { |
| int rc; |
| |
| rc = smb135x_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; |
| } |
| |
| static int usb_current_table_smb1356[] = { |
| 180, |
| 240, |
| 270, |
| 285, |
| 300, |
| 330, |
| 360, |
| 390, |
| 420, |
| 540, |
| 570, |
| 600, |
| 660, |
| 720, |
| 840, |
| 900, |
| 960, |
| 1080, |
| 1110, |
| 1128, |
| 1146, |
| 1170, |
| 1182, |
| 1200, |
| 1230, |
| 1260, |
| 1380, |
| 1440, |
| 1560, |
| 1620, |
| 1680, |
| 1800 |
| }; |
| |
| static int fastchg_current_table[] = { |
| 300, |
| 400, |
| 450, |
| 475, |
| 500, |
| 550, |
| 600, |
| 650, |
| 700, |
| 900, |
| 950, |
| 1000, |
| 1100, |
| 1200, |
| 1400, |
| 2700, |
| 1500, |
| 1600, |
| 1800, |
| 1850, |
| 1880, |
| 1910, |
| 2800, |
| 1950, |
| 1970, |
| 2000, |
| 2050, |
| 2100, |
| 2300, |
| 2400, |
| 2500, |
| 3000 |
| }; |
| |
| static int usb_current_table_smb1357_smb1358[] = { |
| 300, |
| 400, |
| 450, |
| 475, |
| 500, |
| 550, |
| 600, |
| 650, |
| 700, |
| 900, |
| 950, |
| 1000, |
| 1100, |
| 1200, |
| 1400, |
| 1450, |
| 1500, |
| 1600, |
| 1800, |
| 1850, |
| 1880, |
| 1910, |
| 1930, |
| 1950, |
| 1970, |
| 2000, |
| 2050, |
| 2100, |
| 2300, |
| 2400, |
| 2500, |
| 3000 |
| }; |
| |
| static int usb_current_table_smb1359[] = { |
| 300, |
| 400, |
| 450, |
| 475, |
| 500, |
| 550, |
| 600, |
| 650, |
| 700, |
| 900, |
| 950, |
| 1000, |
| 1100, |
| 1200, |
| 1400, |
| 1450, |
| 1500, |
| 1600, |
| 1800, |
| 1850, |
| 1880, |
| 1910, |
| 1930, |
| 1950, |
| 1970, |
| 2000, |
| 2050, |
| 2100, |
| 2300, |
| 2400, |
| 2500 |
| }; |
| |
| static int dc_current_table_smb1356[] = { |
| 180, |
| 240, |
| 270, |
| 285, |
| 300, |
| 330, |
| 360, |
| 390, |
| 420, |
| 540, |
| 570, |
| 600, |
| 660, |
| 720, |
| 840, |
| 870, |
| 900, |
| 960, |
| 1080, |
| 1110, |
| 1128, |
| 1146, |
| 1158, |
| 1170, |
| 1182, |
| 1200, |
| }; |
| |
| static int dc_current_table[] = { |
| 300, |
| 400, |
| 450, |
| 475, |
| 500, |
| 550, |
| 600, |
| 650, |
| 700, |
| 900, |
| 950, |
| 1000, |
| 1100, |
| 1200, |
| 1400, |
| 1450, |
| 1500, |
| 1600, |
| 1800, |
| 1850, |
| 1880, |
| 1910, |
| 1930, |
| 1950, |
| 1970, |
| 2000, |
| }; |
| |
| #define CURRENT_100_MA 100 |
| #define CURRENT_150_MA 150 |
| #define CURRENT_500_MA 500 |
| #define CURRENT_900_MA 900 |
| #define SUSPEND_CURRENT_MA 2 |
| |
| static int __smb135x_usb_suspend(struct smb135x_chg *chip, bool suspend) |
| { |
| int rc; |
| |
| rc = smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_SHUTDOWN_BIT, suspend ? USB_SHUTDOWN_BIT : 0); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't set cfg 11 rc = %d\n", rc); |
| return rc; |
| } |
| |
| static int __smb135x_dc_suspend(struct smb135x_chg *chip, bool suspend) |
| { |
| int rc = 0; |
| |
| rc = smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| DC_SHUTDOWN_BIT, suspend ? DC_SHUTDOWN_BIT : 0); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't set cfg 11 rc = %d\n", rc); |
| return rc; |
| } |
| |
| static int smb135x_path_suspend(struct smb135x_chg *chip, enum path_type path, |
| int reason, bool suspend) |
| { |
| int rc = 0; |
| int suspended; |
| int *path_suspended; |
| int (*func)(struct smb135x_chg *chip, bool suspend); |
| |
| mutex_lock(&chip->path_suspend_lock); |
| if (path == USB) { |
| suspended = chip->usb_suspended; |
| path_suspended = &chip->usb_suspended; |
| func = __smb135x_usb_suspend; |
| } else { |
| suspended = chip->dc_suspended; |
| path_suspended = &chip->dc_suspended; |
| func = __smb135x_dc_suspend; |
| } |
| |
| if (suspend == false) |
| suspended &= ~reason; |
| else |
| suspended |= reason; |
| |
| if (*path_suspended && !suspended) |
| rc = func(chip, 0); |
| if (!(*path_suspended) && suspended) |
| rc = func(chip, 1); |
| |
| if (rc) |
| dev_err(chip->dev, "Couldn't set/unset suspend for %s path rc = %d\n", |
| path == USB ? "usb" : "dc", |
| rc); |
| else |
| *path_suspended = suspended; |
| |
| mutex_unlock(&chip->path_suspend_lock); |
| return rc; |
| } |
| |
| static int smb135x_get_usb_chg_current(struct smb135x_chg *chip) |
| { |
| if (chip->usb_suspended) |
| return SUSPEND_CURRENT_MA; |
| else |
| return chip->real_usb_psy_ma; |
| } |
| #define FCC_MASK SMB135X_MASK(4, 0) |
| #define CFG_1C_REG 0x1C |
| static int smb135x_get_fastchg_current(struct smb135x_chg *chip) |
| { |
| u8 reg; |
| int rc; |
| |
| rc = smb135x_read(chip, CFG_1C_REG, ®); |
| if (rc < 0) { |
| pr_debug("cannot read 1c rc = %d\n", rc); |
| return 0; |
| } |
| reg &= FCC_MASK; |
| if (reg < 0 || chip->fastchg_current_arr_size == 0 |
| || reg > chip->fastchg_current_table[ |
| chip->fastchg_current_arr_size - 1]) { |
| dev_err(chip->dev, "Current table out of range\n"); |
| return -EINVAL; |
| } |
| return chip->fastchg_current_table[reg]; |
| } |
| |
| static int smb135x_set_fastchg_current(struct smb135x_chg *chip, |
| int current_ma) |
| { |
| int i, rc, diff, best, best_diff; |
| u8 reg; |
| |
| /* |
| * if there is no array loaded or if the smallest current limit is |
| * above the requested current, then do nothing |
| */ |
| if (chip->fastchg_current_arr_size == 0) { |
| dev_err(chip->dev, "no table loaded\n"); |
| return -EINVAL; |
| } else if ((current_ma - chip->fastchg_current_table[0]) < 0) { |
| dev_err(chip->dev, "invalid current requested\n"); |
| return -EINVAL; |
| } |
| |
| /* use the closest setting under the requested current */ |
| best = 0; |
| best_diff = current_ma - chip->fastchg_current_table[best]; |
| |
| for (i = 1; i < chip->fastchg_current_arr_size; i++) { |
| diff = current_ma - chip->fastchg_current_table[i]; |
| if (diff >= 0 && diff < best_diff) { |
| best_diff = diff; |
| best = i; |
| } |
| } |
| i = best; |
| |
| reg = i & FCC_MASK; |
| rc = smb135x_masked_write(chip, CFG_1C_REG, FCC_MASK, reg); |
| if (rc < 0) |
| dev_err(chip->dev, "cannot write to config c rc = %d\n", rc); |
| pr_debug("fastchg current set to %dma\n", |
| chip->fastchg_current_table[i]); |
| return rc; |
| } |
| |
| static int smb135x_set_high_usb_chg_current(struct smb135x_chg *chip, |
| int current_ma) |
| { |
| int i, rc; |
| u8 usb_cur_val; |
| |
| for (i = chip->usb_current_arr_size - 1; i >= 0; i--) { |
| if (current_ma >= chip->usb_current_table[i]) |
| break; |
| } |
| if (i < 0) { |
| dev_err(chip->dev, |
| "Cannot find %dma current_table using %d\n", |
| current_ma, CURRENT_150_MA); |
| rc = smb135x_masked_write(chip, CFG_5_REG, |
| USB_2_3_BIT, USB_2_3_BIT); |
| rc |= smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_100_VAL); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't set %dmA rc=%d\n", |
| CURRENT_150_MA, rc); |
| else |
| chip->real_usb_psy_ma = CURRENT_150_MA; |
| return rc; |
| } |
| |
| usb_cur_val = i & USBIN_INPUT_MASK; |
| rc = smb135x_masked_write(chip, CFG_C_REG, |
| USBIN_INPUT_MASK, usb_cur_val); |
| if (rc < 0) { |
| dev_err(chip->dev, "cannot write to config c rc = %d\n", rc); |
| return rc; |
| } |
| |
| rc = smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_AC_VAL); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc); |
| else |
| chip->real_usb_psy_ma = chip->usb_current_table[i]; |
| return rc; |
| } |
| |
| #define MAX_VERSION 0xF |
| #define USB_100_PROBLEM_VERSION 0x2 |
| /* if APSD results are used |
| * if SDP is detected it will look at 500mA setting |
| * if set it will draw 500mA |
| * if unset it will draw 100mA |
| * if CDP/DCP it will look at 0x0C setting |
| * i.e. values in 0x41[1, 0] does not matter |
| */ |
| static int smb135x_set_usb_chg_current(struct smb135x_chg *chip, |
| int current_ma) |
| { |
| int rc; |
| |
| pr_debug("USB current_ma = %d\n", current_ma); |
| |
| if (chip->workaround_flags & WRKARND_USB100_BIT) { |
| pr_info("USB requested = %dmA using %dmA\n", current_ma, |
| CURRENT_500_MA); |
| current_ma = CURRENT_500_MA; |
| } |
| |
| if (current_ma == 0) |
| /* choose the lowest available value of 100mA */ |
| current_ma = CURRENT_100_MA; |
| |
| if (current_ma == SUSPEND_CURRENT_MA) { |
| /* force suspend bit */ |
| rc = smb135x_path_suspend(chip, USB, CURRENT, true); |
| chip->real_usb_psy_ma = SUSPEND_CURRENT_MA; |
| goto out; |
| } |
| if (current_ma < CURRENT_150_MA) { |
| /* force 100mA */ |
| rc = smb135x_masked_write(chip, CFG_5_REG, USB_2_3_BIT, 0); |
| rc |= smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_100_VAL); |
| rc |= smb135x_path_suspend(chip, USB, CURRENT, false); |
| chip->real_usb_psy_ma = CURRENT_100_MA; |
| goto out; |
| } |
| /* specific current values */ |
| if (current_ma == CURRENT_150_MA) { |
| rc = smb135x_masked_write(chip, CFG_5_REG, |
| USB_2_3_BIT, USB_2_3_BIT); |
| rc |= smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_100_VAL); |
| rc |= smb135x_path_suspend(chip, USB, CURRENT, false); |
| chip->real_usb_psy_ma = CURRENT_150_MA; |
| goto out; |
| } |
| if (current_ma == CURRENT_500_MA) { |
| rc = smb135x_masked_write(chip, CFG_5_REG, USB_2_3_BIT, 0); |
| rc |= smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_500_VAL); |
| rc |= smb135x_path_suspend(chip, USB, CURRENT, false); |
| chip->real_usb_psy_ma = CURRENT_500_MA; |
| goto out; |
| } |
| if (current_ma == CURRENT_900_MA) { |
| rc = smb135x_masked_write(chip, CFG_5_REG, |
| USB_2_3_BIT, USB_2_3_BIT); |
| rc |= smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USB_100_500_AC_MASK, USB_500_VAL); |
| rc |= smb135x_path_suspend(chip, USB, CURRENT, false); |
| chip->real_usb_psy_ma = CURRENT_900_MA; |
| goto out; |
| } |
| |
| rc = smb135x_set_high_usb_chg_current(chip, current_ma); |
| rc |= smb135x_path_suspend(chip, USB, CURRENT, false); |
| out: |
| if (rc < 0) |
| dev_err(chip->dev, |
| "Couldn't set %dmA rc = %d\n", current_ma, rc); |
| return rc; |
| } |
| |
| static int smb135x_set_dc_chg_current(struct smb135x_chg *chip, |
| int current_ma) |
| { |
| int i, rc; |
| u8 dc_cur_val; |
| |
| for (i = chip->dc_current_arr_size - 1; i >= 0; i--) { |
| if (chip->dc_psy_ma >= chip->dc_current_table[i]) |
| break; |
| } |
| dc_cur_val = i & DCIN_INPUT_MASK; |
| rc = smb135x_masked_write(chip, CFG_A_REG, |
| DCIN_INPUT_MASK, dc_cur_val); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't set dc charge current rc = %d\n", |
| rc); |
| return rc; |
| } |
| return 0; |
| } |
| |
| static int smb135x_set_appropriate_current(struct smb135x_chg *chip, |
| enum path_type path) |
| { |
| int therm_ma, current_ma; |
| int path_current = (path == USB) ? chip->usb_psy_ma : chip->dc_psy_ma; |
| int (*func)(struct smb135x_chg *chip, int current_ma); |
| int rc = 0; |
| |
| if (!chip->usb_psy && path == USB) |
| return 0; |
| |
| /* |
| * 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 (path == USB) { |
| path_current = chip->usb_psy_ma; |
| func = smb135x_set_usb_chg_current; |
| } else { |
| path_current = chip->dc_psy_ma; |
| func = smb135x_set_dc_chg_current; |
| if (chip->dc_psy_type == -EINVAL) |
| func = NULL; |
| } |
| |
| 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 (func != NULL) |
| rc = func(chip, current_ma); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't set %s current to min(%d, %d)rc = %d\n", |
| path == USB ? "usb" : "dc", |
| therm_ma, path_current, |
| rc); |
| return rc; |
| } |
| |
| static int smb135x_charging_enable(struct smb135x_chg *chip, int enable) |
| { |
| int rc; |
| |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, |
| CMD_CHG_EN, enable ? CMD_CHG_EN : 0); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set CHG_ENABLE_BIT enable = %d rc = %d\n", |
| enable, rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int __smb135x_charging(struct smb135x_chg *chip, int enable) |
| { |
| int rc = 0; |
| |
| pr_debug("charging enable = %d\n", enable); |
| |
| if (chip->chg_disabled_permanently) { |
| pr_debug("charging is disabled permanetly\n"); |
| return -EINVAL; |
| } |
| |
| rc = smb135x_charging_enable(chip, enable); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't %s charging rc = %d\n", |
| enable ? "enable" : "disable", rc); |
| return rc; |
| } |
| chip->chg_enabled = enable; |
| |
| /* set the suspended status */ |
| rc = smb135x_path_suspend(chip, DC, USER, !enable); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set dc suspend to %d rc = %d\n", |
| enable, rc); |
| return rc; |
| } |
| rc = smb135x_path_suspend(chip, USB, USER, !enable); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set usb suspend to %d rc = %d\n", |
| enable, rc); |
| return rc; |
| } |
| |
| pr_debug("charging %s\n", |
| enable ? "enabled" : "disabled running from batt"); |
| return rc; |
| } |
| |
| static int smb135x_charging(struct smb135x_chg *chip, int enable) |
| { |
| int rc = 0; |
| |
| pr_debug("charging enable = %d\n", enable); |
| |
| __smb135x_charging(chip, enable); |
| |
| if (chip->usb_psy) { |
| pr_debug("usb psy changed\n"); |
| power_supply_changed(chip->usb_psy); |
| } |
| if (chip->dc_psy_type != -EINVAL) { |
| pr_debug("dc psy changed\n"); |
| power_supply_changed(&chip->dc_psy); |
| } |
| pr_debug("charging %s\n", |
| enable ? "enabled" : "disabled running from batt"); |
| return rc; |
| } |
| |
| static int smb135x_system_temp_level_set(struct smb135x_chg *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)) { |
| /* |
| * Disable charging if highest value selected by |
| * setting the DC and USB path in suspend |
| */ |
| rc = smb135x_path_suspend(chip, DC, THERMAL, true); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set dc suspend rc %d\n", rc); |
| goto out; |
| } |
| rc = smb135x_path_suspend(chip, USB, THERMAL, true); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set usb suspend rc %d\n", rc); |
| goto out; |
| } |
| goto out; |
| } |
| |
| smb135x_set_appropriate_current(chip, USB); |
| smb135x_set_appropriate_current(chip, DC); |
| |
| if (prev_therm_lvl == chip->thermal_levels - 1) { |
| /* |
| * If previously highest value was selected charging must have |
| * been disabed. Enable charging by taking the DC and USB path |
| * out of suspend. |
| */ |
| rc = smb135x_path_suspend(chip, DC, THERMAL, false); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set dc suspend rc %d\n", rc); |
| goto out; |
| } |
| rc = smb135x_path_suspend(chip, USB, THERMAL, false); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set usb suspend rc %d\n", rc); |
| goto out; |
| } |
| } |
| out: |
| mutex_unlock(&chip->current_change_lock); |
| return rc; |
| } |
| |
| static int smb135x_battery_set_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| const union power_supply_propval *val) |
| { |
| int rc = 0, update_psy = 0; |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, batt_psy); |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_STATUS: |
| if (!chip->bms_controlled_charging) { |
| rc = -EINVAL; |
| break; |
| } |
| switch (val->intval) { |
| case POWER_SUPPLY_STATUS_FULL: |
| rc = smb135x_charging_enable(chip, false); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't disable charging rc = %d\n", |
| rc); |
| } else { |
| chip->chg_done_batt_full = true; |
| update_psy = 1; |
| dev_dbg(chip->dev, "status = FULL chg_done_batt_full = %d", |
| chip->chg_done_batt_full); |
| } |
| break; |
| case POWER_SUPPLY_STATUS_DISCHARGING: |
| chip->chg_done_batt_full = false; |
| update_psy = 1; |
| dev_dbg(chip->dev, "status = DISCHARGING chg_done_batt_full = %d", |
| chip->chg_done_batt_full); |
| break; |
| case POWER_SUPPLY_STATUS_CHARGING: |
| rc = smb135x_charging_enable(chip, true); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't enable charging rc = %d\n", |
| rc); |
| } else { |
| chip->chg_done_batt_full = false; |
| dev_dbg(chip->dev, "status = CHARGING chg_done_batt_full = %d", |
| chip->chg_done_batt_full); |
| } |
| break; |
| default: |
| update_psy = 0; |
| rc = -EINVAL; |
| } |
| break; |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| smb135x_charging(chip, val->intval); |
| break; |
| case POWER_SUPPLY_PROP_CAPACITY: |
| chip->fake_battery_soc = val->intval; |
| update_psy = 1; |
| break; |
| case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: |
| smb135x_system_temp_level_set(chip, val->intval); |
| break; |
| default: |
| rc = -EINVAL; |
| } |
| |
| if (!rc && update_psy) |
| power_supply_changed(&chip->batt_psy); |
| return rc; |
| } |
| |
| static int smb135x_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 smb135x_battery_get_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| union power_supply_propval *val) |
| { |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, batt_psy); |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_STATUS: |
| val->intval = smb135x_get_prop_batt_status(chip); |
| break; |
| case POWER_SUPPLY_PROP_PRESENT: |
| val->intval = smb135x_get_prop_batt_present(chip); |
| break; |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| val->intval = chip->chg_enabled; |
| break; |
| case POWER_SUPPLY_PROP_CHARGE_TYPE: |
| val->intval = smb135x_get_prop_charge_type(chip); |
| break; |
| case POWER_SUPPLY_PROP_CAPACITY: |
| val->intval = smb135x_get_prop_batt_capacity(chip); |
| break; |
| case POWER_SUPPLY_PROP_HEALTH: |
| val->intval = smb135x_get_prop_batt_health(chip); |
| break; |
| case POWER_SUPPLY_PROP_TECHNOLOGY: |
| val->intval = POWER_SUPPLY_TECHNOLOGY_LION; |
| break; |
| case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: |
| val->intval = chip->therm_lvl_sel; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static enum power_supply_property smb135x_dc_properties[] = { |
| POWER_SUPPLY_PROP_PRESENT, |
| POWER_SUPPLY_PROP_ONLINE, |
| POWER_SUPPLY_PROP_HEALTH, |
| }; |
| |
| static int smb135x_dc_get_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| union power_supply_propval *val) |
| { |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, dc_psy); |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_PRESENT: |
| val->intval = chip->dc_present; |
| break; |
| case POWER_SUPPLY_PROP_ONLINE: |
| val->intval = chip->chg_enabled ? chip->dc_present : 0; |
| break; |
| case POWER_SUPPLY_PROP_HEALTH: |
| val->intval = chip->dc_present; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| #define MIN_FLOAT_MV 3600 |
| #define MAX_FLOAT_MV 4500 |
| |
| #define MID_RANGE_FLOAT_MV_MIN 3600 |
| #define MID_RANGE_FLOAT_MIN_VAL 0x05 |
| #define MID_RANGE_FLOAT_STEP_MV 20 |
| |
| #define HIGH_RANGE_FLOAT_MIN_MV 4340 |
| #define HIGH_RANGE_FLOAT_MIN_VAL 0x2A |
| #define HIGH_RANGE_FLOAT_STEP_MV 10 |
| |
| #define VHIGH_RANGE_FLOAT_MIN_MV 4400 |
| #define VHIGH_RANGE_FLOAT_MIN_VAL 0x2E |
| #define VHIGH_RANGE_FLOAT_STEP_MV 20 |
| static int smb135x_float_voltage_set(struct smb135x_chg *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; |
| } |
| |
| if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) { |
| /* mid range */ |
| temp = MID_RANGE_FLOAT_MIN_VAL |
| + (vfloat_mv - MID_RANGE_FLOAT_MV_MIN) |
| / MID_RANGE_FLOAT_STEP_MV; |
| } else if (vfloat_mv < VHIGH_RANGE_FLOAT_MIN_MV) { |
| /* high range */ |
| temp = HIGH_RANGE_FLOAT_MIN_VAL |
| + (vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV) |
| / HIGH_RANGE_FLOAT_STEP_MV; |
| } else { |
| /* very high range */ |
| temp = VHIGH_RANGE_FLOAT_MIN_VAL |
| + (vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV) |
| / VHIGH_RANGE_FLOAT_STEP_MV; |
| } |
| |
| return smb135x_write(chip, VFLOAT_REG, temp); |
| } |
| |
| static int smb135x_set_resume_threshold(struct smb135x_chg *chip, |
| int resume_delta_mv) |
| { |
| int rc; |
| u8 reg; |
| |
| if (!chip->inhibit_disabled) { |
| if (resume_delta_mv < 100) |
| reg = CHG_INHIBIT_50MV_VAL; |
| else if (resume_delta_mv < 200) |
| reg = CHG_INHIBIT_100MV_VAL; |
| else if (resume_delta_mv < 300) |
| reg = CHG_INHIBIT_200MV_VAL; |
| else |
| reg = CHG_INHIBIT_300MV_VAL; |
| |
| rc = smb135x_masked_write(chip, CFG_4_REG, CHG_INHIBIT_MASK, |
| reg); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n", |
| rc); |
| return rc; |
| } |
| } |
| |
| if (resume_delta_mv < 200) |
| reg = 0; |
| else |
| reg = RECHARGE_200MV_BIT; |
| |
| rc = smb135x_masked_write(chip, CFG_5_REG, RECHARGE_200MV_BIT, reg); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't set recharge rc = %d\n", rc); |
| return rc; |
| } |
| return 0; |
| } |
| |
| static enum power_supply_property smb135x_parallel_properties[] = { |
| POWER_SUPPLY_PROP_CHARGING_ENABLED, |
| POWER_SUPPLY_PROP_STATUS, |
| POWER_SUPPLY_PROP_PRESENT, |
| POWER_SUPPLY_PROP_CURRENT_MAX, |
| POWER_SUPPLY_PROP_VOLTAGE_MAX, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, |
| }; |
| |
| static int smb135x_parallel_set_chg_present(struct smb135x_chg *chip, |
| int present) |
| { |
| u8 val; |
| int rc; |
| |
| /* Check if SMB135x is present */ |
| rc = smb135x_read(chip, VERSION1_REG, &val); |
| if (rc) { |
| pr_debug("Failed to detect smb135x-parallel charger may be absent\n"); |
| return -ENODEV; |
| } |
| |
| if (present == chip->parallel_charger_present) { |
| pr_debug("present %d -> %d, skipping\n", |
| chip->parallel_charger_present, present); |
| return 0; |
| } |
| |
| if (present) { |
| rc = smb135x_enable_volatile_writes(chip); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't configure for volatile rc = %d\n", |
| rc); |
| return rc; |
| } |
| |
| /* set the float voltage */ |
| if (chip->vfloat_mv != -EINVAL) { |
| rc = smb135x_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; |
| } |
| } |
| |
| /* resume threshold */ |
| if (chip->resume_delta_mv != -EINVAL) { |
| smb135x_set_resume_threshold(chip, |
| chip->resume_delta_mv); |
| } |
| |
| rc = smb135x_masked_write(chip, CMD_INPUT_LIMIT, |
| USE_REGISTER_FOR_CURRENT, |
| USE_REGISTER_FOR_CURRENT); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set input limit cmd rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| /* set chg en by pin active low and enable auto recharge */ |
| rc = smb135x_masked_write(chip, CFG_14_REG, |
| CHG_EN_BY_PIN_BIT | CHG_EN_ACTIVE_LOW_BIT |
| | DISABLE_AUTO_RECHARGE_BIT, |
| CHG_EN_BY_PIN_BIT | CHG_EN_ACTIVE_LOW_BIT); |
| |
| /* set bit 0 = 100mA bit 1 = 500mA and set register control */ |
| rc = smb135x_masked_write(chip, CFG_E_REG, |
| POLARITY_100_500_BIT | USB_CTRL_BY_PIN_BIT, |
| POLARITY_100_500_BIT); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't set usbin cfg rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| /* control USB suspend via command bits */ |
| rc = smb135x_masked_write(chip, USBIN_DCIN_CFG_REG, |
| USBIN_SUSPEND_VIA_COMMAND_BIT, |
| USBIN_SUSPEND_VIA_COMMAND_BIT); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't set cfg rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* set the fastchg_current to the lowest setting */ |
| if (chip->fastchg_current_arr_size > 0) |
| rc = smb135x_set_fastchg_current(chip, |
| chip->fastchg_current_table[0]); |
| |
| /* |
| * enforce chip->chg_enabled since this could be the first |
| * time we have i2c access to the charger after |
| * chip->chg_enabled has been modified |
| */ |
| smb135x_charging(chip, chip->chg_enabled); |
| } |
| |
| chip->parallel_charger_present = present; |
| /* |
| * When present is being set force USB suspend, start charging |
| * only when CURRENT_MAX is set. |
| * |
| * Usually the chip will be shutdown (no i2c access to the chip) |
| * when USB is removed, however there could be situations when |
| * it is not. To cover for USB reinsetions in such situations |
| * force USB suspend when present is being unset. |
| * It is likely that i2c access could fail here - do not return error. |
| * (It is not possible to detect whether the chip is in shutdown state |
| * or not except for the i2c error). |
| */ |
| chip->usb_psy_ma = SUSPEND_CURRENT_MA; |
| rc = smb135x_path_suspend(chip, USB, CURRENT, true); |
| |
| if (present) { |
| if (rc) { |
| dev_err(chip->dev, |
| "Couldn't set usb suspend to true rc = %d\n", |
| rc); |
| return rc; |
| } |
| /* Check if the USB is configured for suspend. If not, do it */ |
| mutex_lock(&chip->path_suspend_lock); |
| rc = smb135x_read(chip, CMD_INPUT_LIMIT, &val); |
| if (rc) { |
| dev_err(chip->dev, |
| "Couldn't read 0x%02x rc:%d\n", CMD_INPUT_LIMIT, |
| rc); |
| mutex_unlock(&chip->path_suspend_lock); |
| return rc; |
| } else if (!(val & BIT(6))) { |
| rc = __smb135x_usb_suspend(chip, 1); |
| } |
| mutex_unlock(&chip->path_suspend_lock); |
| if (rc) { |
| dev_err(chip->dev, |
| "Couldn't set usb to suspend rc:%d\n", rc); |
| return rc; |
| } |
| } else { |
| chip->real_usb_psy_ma = SUSPEND_CURRENT_MA; |
| } |
| return 0; |
| } |
| |
| static int smb135x_parallel_set_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| const union power_supply_propval *val) |
| { |
| int rc = 0; |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, parallel_psy); |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| if (chip->parallel_charger_present) |
| smb135x_charging(chip, val->intval); |
| else |
| chip->chg_enabled = val->intval; |
| break; |
| case POWER_SUPPLY_PROP_PRESENT: |
| rc = smb135x_parallel_set_chg_present(chip, val->intval); |
| break; |
| case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
| if (chip->parallel_charger_present) { |
| rc = smb135x_set_fastchg_current(chip, |
| val->intval / 1000); |
| } |
| break; |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| if (chip->parallel_charger_present) { |
| chip->usb_psy_ma = val->intval / 1000; |
| rc = smb135x_set_usb_chg_current(chip, |
| chip->usb_psy_ma); |
| } |
| break; |
| case POWER_SUPPLY_PROP_VOLTAGE_MAX: |
| if (chip->parallel_charger_present && |
| (chip->vfloat_mv != val->intval)) { |
| rc = smb135x_float_voltage_set(chip, val->intval); |
| if (!rc) |
| chip->vfloat_mv = val->intval; |
| } else { |
| chip->vfloat_mv = val->intval; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| return rc; |
| } |
| |
| static int smb135x_parallel_is_writeable(struct power_supply *psy, |
| enum power_supply_property prop) |
| { |
| int rc; |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| rc = 1; |
| break; |
| default: |
| rc = 0; |
| break; |
| } |
| return rc; |
| } |
| static int smb135x_parallel_get_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| union power_supply_propval *val) |
| { |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, parallel_psy); |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| val->intval = chip->chg_enabled; |
| break; |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| if (chip->parallel_charger_present) |
| val->intval = smb135x_get_usb_chg_current(chip) * 1000; |
| else |
| val->intval = 0; |
| break; |
| case POWER_SUPPLY_PROP_VOLTAGE_MAX: |
| val->intval = chip->vfloat_mv; |
| break; |
| case POWER_SUPPLY_PROP_PRESENT: |
| val->intval = chip->parallel_charger_present; |
| break; |
| case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
| if (chip->parallel_charger_present) |
| val->intval = smb135x_get_fastchg_current(chip) * 1000; |
| else |
| val->intval = 0; |
| break; |
| case POWER_SUPPLY_PROP_STATUS: |
| if (chip->parallel_charger_present) |
| val->intval = smb135x_get_prop_batt_status(chip); |
| else |
| val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static void smb135x_external_power_changed(struct power_supply *psy) |
| { |
| struct smb135x_chg *chip = container_of(psy, |
| struct smb135x_chg, batt_psy); |
| union power_supply_propval prop = {0,}; |
| int rc, current_limit = 0; |
| |
| if (!chip->usb_psy) |
| return; |
| |
| if (chip->bms_psy_name) |
| chip->bms_psy = |
| power_supply_get_by_name((char *)chip->bms_psy_name); |
| |
| 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 = smb135x_set_appropriate_current(chip, USB); |
| mutex_unlock(&chip->current_change_lock); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't set usb current rc = %d\n", |
| rc); |
| } |
| |
| rc = chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_ONLINE, &prop); |
| if (rc < 0) |
| dev_err(chip->dev, |
| "could not read USB ONLINE property, rc=%d\n", rc); |
| |
| /* update online property */ |
| rc = 0; |
| if (chip->usb_present && chip->chg_enabled && 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) |
| dev_err(chip->dev, "could not set usb online, rc=%d\n", rc); |
| } |
| |
| static bool elapsed_msec_greater(struct timeval *start_time, |
| struct timeval *end_time, int ms) |
| { |
| int msec_elapsed; |
| |
| msec_elapsed = (end_time->tv_sec - start_time->tv_sec) * 1000 + |
| DIV_ROUND_UP(end_time->tv_usec - start_time->tv_usec, 1000); |
| |
| return (msec_elapsed > ms); |
| } |
| |
| #define MAX_STEP_MS 10 |
| static int smb135x_chg_otg_enable(struct smb135x_chg *chip) |
| { |
| int rc = 0; |
| int restart_count = 0; |
| struct timeval time_a, time_b, time_c, time_d; |
| u8 reg; |
| |
| if (chip->revision == REV_2) { |
| /* |
| * Workaround for a hardware bug where the OTG needs to be |
| * enabled disabled and enabled for it to be actually enabled. |
| * The time between each step should be atmost MAX_STEP_MS |
| * |
| * Note that if enable-disable executes within the timeframe |
| * but the final enable takes more than MAX_STEP_ME, we treat |
| * it as the first enable and try disabling again. We don't |
| * want to issue enable back to back. |
| * |
| * Notice the instances when time is captured and the |
| * successive steps. |
| * timeA-enable-timeC-disable-timeB-enable-timeD. |
| * When |
| * (timeB - timeA) < MAX_STEP_MS AND |
| * (timeC - timeD) < MAX_STEP_MS |
| * then it is guaranteed that the successive steps |
| * must have executed within MAX_STEP_MS |
| */ |
| do_gettimeofday(&time_a); |
| restart_from_enable: |
| /* first step - enable otg */ |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, OTG_EN); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| restart_from_disable: |
| /* second step - disable otg */ |
| do_gettimeofday(&time_c); |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, 0); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", |
| rc); |
| return rc; |
| } |
| do_gettimeofday(&time_b); |
| |
| if (elapsed_msec_greater(&time_a, &time_b, MAX_STEP_MS)) { |
| restart_count++; |
| if (restart_count > 10) { |
| dev_err(chip->dev, |
| "Couldn't enable OTG restart_count=%d\n", |
| restart_count); |
| return -EAGAIN; |
| } |
| time_a = time_b; |
| pr_debug("restarting from first enable\n"); |
| goto restart_from_enable; |
| } |
| |
| /* third step (first step in case of a failure) - enable otg */ |
| time_a = time_b; |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, OTG_EN); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", |
| rc); |
| return rc; |
| } |
| do_gettimeofday(&time_d); |
| |
| if (elapsed_msec_greater(&time_c, &time_d, MAX_STEP_MS)) { |
| restart_count++; |
| if (restart_count > 10) { |
| dev_err(chip->dev, |
| "Couldn't enable OTG restart_count=%d\n", |
| restart_count); |
| return -EAGAIN; |
| } |
| pr_debug("restarting from disable\n"); |
| goto restart_from_disable; |
| } |
| } else { |
| rc = smb135x_read(chip, CMD_CHG_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read cmd reg rc=%d\n", |
| rc); |
| return rc; |
| } |
| if (reg & OTG_EN) { |
| /* if it is set, disable it before re-enabling it */ |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, 0); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n", |
| rc); |
| return rc; |
| } |
| } |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, OTG_EN); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", |
| rc); |
| return rc; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int smb135x_chg_otg_regulator_enable(struct regulator_dev *rdev) |
| { |
| int rc = 0; |
| struct smb135x_chg *chip = rdev_get_drvdata(rdev); |
| |
| chip->otg_oc_count = 0; |
| rc = smb135x_chg_otg_enable(chip); |
| if (rc) |
| dev_err(chip->dev, "Couldn't enable otg regulator rc=%d\n", rc); |
| |
| return rc; |
| } |
| |
| static int smb135x_chg_otg_regulator_disable(struct regulator_dev *rdev) |
| { |
| int rc = 0; |
| struct smb135x_chg *chip = rdev_get_drvdata(rdev); |
| |
| mutex_lock(&chip->otg_oc_count_lock); |
| cancel_delayed_work_sync(&chip->reset_otg_oc_count_work); |
| mutex_unlock(&chip->otg_oc_count_lock); |
| rc = smb135x_masked_write(chip, CMD_CHG_REG, OTG_EN, 0); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n", rc); |
| return rc; |
| } |
| |
| static int smb135x_chg_otg_regulator_is_enable(struct regulator_dev *rdev) |
| { |
| int rc = 0; |
| u8 reg = 0; |
| struct smb135x_chg *chip = rdev_get_drvdata(rdev); |
| |
| rc = smb135x_read(chip, CMD_CHG_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't read OTG enable bit rc=%d\n", rc); |
| return rc; |
| } |
| |
| return (reg & OTG_EN) ? 1 : 0; |
| } |
| |
| struct regulator_ops smb135x_chg_otg_reg_ops = { |
| .enable = smb135x_chg_otg_regulator_enable, |
| .disable = smb135x_chg_otg_regulator_disable, |
| .is_enabled = smb135x_chg_otg_regulator_is_enable, |
| }; |
| |
| static int smb135x_set_current_tables(struct smb135x_chg *chip) |
| { |
| switch (chip->version) { |
| case V_SMB1356: |
| chip->usb_current_table = usb_current_table_smb1356; |
| chip->usb_current_arr_size |
| = ARRAY_SIZE(usb_current_table_smb1356); |
| chip->dc_current_table = dc_current_table_smb1356; |
| chip->dc_current_arr_size |
| = ARRAY_SIZE(dc_current_table_smb1356); |
| chip->fastchg_current_table = NULL; |
| chip->fastchg_current_arr_size = 0; |
| break; |
| case V_SMB1357: |
| chip->usb_current_table = usb_current_table_smb1357_smb1358; |
| chip->usb_current_arr_size |
| = ARRAY_SIZE(usb_current_table_smb1357_smb1358); |
| chip->dc_current_table = dc_current_table; |
| chip->dc_current_arr_size = ARRAY_SIZE(dc_current_table); |
| chip->fastchg_current_table = fastchg_current_table; |
| chip->fastchg_current_arr_size |
| = ARRAY_SIZE(fastchg_current_table); |
| break; |
| case V_SMB1358: |
| chip->usb_current_table = usb_current_table_smb1357_smb1358; |
| chip->usb_current_arr_size |
| = ARRAY_SIZE(usb_current_table_smb1357_smb1358); |
| chip->dc_current_table = dc_current_table; |
| chip->dc_current_arr_size = ARRAY_SIZE(dc_current_table); |
| chip->fastchg_current_table = NULL; |
| chip->fastchg_current_arr_size = 0; |
| break; |
| case V_SMB1359: |
| chip->usb_current_table = usb_current_table_smb1359; |
| chip->usb_current_arr_size |
| = ARRAY_SIZE(usb_current_table_smb1359); |
| chip->dc_current_table = dc_current_table; |
| chip->dc_current_arr_size = ARRAY_SIZE(dc_current_table); |
| chip->fastchg_current_table = NULL; |
| chip->fastchg_current_arr_size = 0; |
| break; |
| } |
| return 0; |
| } |
| |
| #define SMB1356_VERSION3_BIT BIT(7) |
| #define SMB1357_VERSION1_VAL 0x01 |
| #define SMB1358_VERSION1_VAL 0x02 |
| #define SMB1359_VERSION1_VAL 0x00 |
| #define SMB1357_VERSION2_VAL 0x01 |
| #define SMB1358_VERSION2_VAL 0x02 |
| #define SMB1359_VERSION2_VAL 0x00 |
| static int smb135x_chip_version_and_revision(struct smb135x_chg *chip) |
| { |
| int rc; |
| u8 version1, version2, version3; |
| |
| /* read the revision */ |
| rc = read_revision(chip, &chip->revision); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read revision rc = %d\n", rc); |
| return rc; |
| } |
| |
| if (chip->revision >= REV_MAX || revision_str[chip->revision] == NULL) { |
| dev_err(chip->dev, "Bad revision found = %d\n", chip->revision); |
| return -EINVAL; |
| } |
| |
| /* check if it is smb1356 */ |
| rc = read_version3(chip, &version3); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read version3 rc = %d\n", rc); |
| return rc; |
| } |
| |
| if (version3 & SMB1356_VERSION3_BIT) { |
| chip->version = V_SMB1356; |
| goto wrkarnd_and_input_current_values; |
| } |
| |
| /* check if it is smb1357, smb1358 or smb1359 based on revision */ |
| if (chip->revision <= REV_1_1) { |
| rc = read_version1(chip, &version1); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't read version 1 rc = %d\n", rc); |
| return rc; |
| } |
| switch (version1) { |
| case SMB1357_VERSION1_VAL: |
| chip->version = V_SMB1357; |
| break; |
| case SMB1358_VERSION1_VAL: |
| chip->version = V_SMB1358; |
| break; |
| case SMB1359_VERSION1_VAL: |
| chip->version = V_SMB1359; |
| break; |
| default: |
| dev_err(chip->dev, |
| "Unknown version 1 = 0x%02x rc = %d\n", |
| version1, rc); |
| return rc; |
| } |
| } else { |
| rc = read_version2(chip, &version2); |
| if (rc < 0) { |
| dev_err(chip->dev, |
| "Couldn't read version 2 rc = %d\n", rc); |
| return rc; |
| } |
| switch (version2) { |
| case SMB1357_VERSION2_VAL: |
| chip->version = V_SMB1357; |
| break; |
| case SMB1358_VERSION2_VAL: |
| chip->version = V_SMB1358; |
| break; |
| case SMB1359_VERSION2_VAL: |
| chip->version = V_SMB1359; |
| break; |
| default: |
| dev_err(chip->dev, |
| "Unknown version 2 = 0x%02x rc = %d\n", |
| version2, rc); |
| return rc; |
| } |
| } |
| |
| wrkarnd_and_input_current_values: |
| if (is_usb100_broken(chip)) |
| chip->workaround_flags |= WRKARND_USB100_BIT; |
| /* |
| * Rev v1.0 and v1.1 of SMB135x fails charger type detection |
| * (apsd) due to interference on the D+/- lines by the USB phy. |
| * Set the workaround flag to disable charger type reporting |
| * for this revision. |
| */ |
| if (chip->revision <= REV_1_1) |
| chip->workaround_flags |= WRKARND_APSD_FAIL; |
| |
| pr_debug("workaround_flags = %x\n", chip->workaround_flags); |
| |
| return smb135x_set_current_tables(chip); |
| } |
| |
| static int smb135x_regulator_init(struct smb135x_chg *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 = &smb135x_chg_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 void smb135x_regulator_deinit(struct smb135x_chg *chip) |
| { |
| if (chip->otg_vreg.rdev) |
| regulator_unregister(chip->otg_vreg.rdev); |
| } |
| |
| static void wireless_insertion_work(struct work_struct *work) |
| { |
| struct smb135x_chg *chip = |
| container_of(work, struct smb135x_chg, |
| wireless_insertion_work.work); |
| |
| /* unsuspend dc */ |
| smb135x_path_suspend(chip, DC, CURRENT, false); |
| } |
| |
| static int hot_hard_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| chip->batt_hot = !!rt_stat; |
| return 0; |
| } |
| static int cold_hard_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| chip->batt_cold = !!rt_stat; |
| return 0; |
| } |
| static int hot_soft_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| chip->batt_warm = !!rt_stat; |
| return 0; |
| } |
| static int cold_soft_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| chip->batt_cool = !!rt_stat; |
| return 0; |
| } |
| static int battery_missing_handler(struct smb135x_chg *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 smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_warn("vbat low\n"); |
| return 0; |
| } |
| static int chg_hot_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_warn("chg hot\n"); |
| return 0; |
| } |
| static int chg_term_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| |
| /* |
| * This handler gets called even when the charger based termination |
| * is disabled (due to change in RT status). However, in a bms |
| * controlled design the battery status should not be updated. |
| */ |
| if (!chip->iterm_disabled) |
| chip->chg_done_batt_full = !!rt_stat; |
| return 0; |
| } |
| |
| static int taper_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| return 0; |
| } |
| |
| static int fast_chg_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| |
| if (rt_stat & IRQ_C_FASTCHG_BIT) |
| chip->chg_done_batt_full = false; |
| |
| return 0; |
| } |
| |
| static int recharge_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| int rc; |
| |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| |
| if (chip->bms_controlled_charging) { |
| rc = smb135x_charging_enable(chip, true); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't enable charging rc = %d\n", |
| rc); |
| } |
| |
| return 0; |
| } |
| |
| static int safety_timeout_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_warn("safety timeout rt_stat = 0x%02x\n", rt_stat); |
| return 0; |
| } |
| |
| /** |
| * power_ok_handler() - called when the switcher turns on or turns off |
| * @chip: pointer to smb135x_chg chip |
| * @rt_stat: the status bit indicating switcher turning on or off |
| */ |
| static int power_ok_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| return 0; |
| } |
| |
| static int rid_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| bool usb_slave_present; |
| |
| usb_slave_present = is_usb_slave_present(chip); |
| |
| if (chip->usb_slave_present ^ usb_slave_present) { |
| chip->usb_slave_present = usb_slave_present; |
| if (chip->usb_psy) { |
| pr_debug("setting usb psy usb_otg = %d\n", |
| chip->usb_slave_present); |
| power_supply_set_usb_otg(chip->usb_psy, |
| chip->usb_slave_present); |
| } |
| } |
| return 0; |
| } |
| |
| #define RESET_OTG_OC_COUNT_MS 100 |
| static void reset_otg_oc_count_work(struct work_struct *work) |
| { |
| struct smb135x_chg *chip = |
| container_of(work, struct smb135x_chg, |
| reset_otg_oc_count_work.work); |
| |
| mutex_lock(&chip->otg_oc_count_lock); |
| pr_debug("It has been %dmS since OverCurrent interrupt resetting the count\n", |
| RESET_OTG_OC_COUNT_MS); |
| chip->otg_oc_count = 0; |
| mutex_unlock(&chip->otg_oc_count_lock); |
| } |
| |
| #define MAX_OTG_RETRY 3 |
| static int otg_oc_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| int rc; |
| |
| mutex_lock(&chip->otg_oc_count_lock); |
| cancel_delayed_work_sync(&chip->reset_otg_oc_count_work); |
| ++chip->otg_oc_count; |
| if (chip->otg_oc_count < MAX_OTG_RETRY) { |
| rc = smb135x_chg_otg_enable(chip); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", |
| rc); |
| } else { |
| pr_warn_ratelimited("Tried enabling OTG %d times, the USB slave is nonconformant.\n", |
| chip->otg_oc_count); |
| } |
| |
| pr_debug("rt_stat = 0x%02x\n", rt_stat); |
| schedule_delayed_work(&chip->reset_otg_oc_count_work, |
| msecs_to_jiffies(RESET_OTG_OC_COUNT_MS)); |
| mutex_unlock(&chip->otg_oc_count_lock); |
| return 0; |
| } |
| |
| static int handle_dc_removal(struct smb135x_chg *chip) |
| { |
| if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) { |
| cancel_delayed_work_sync(&chip->wireless_insertion_work); |
| smb135x_path_suspend(chip, DC, CURRENT, true); |
| } |
| |
| if (chip->dc_psy_type != -EINVAL) |
| power_supply_set_online(&chip->dc_psy, chip->dc_present); |
| return 0; |
| } |
| |
| #define DCIN_UNSUSPEND_DELAY_MS 1000 |
| static int handle_dc_insertion(struct smb135x_chg *chip) |
| { |
| if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) |
| schedule_delayed_work(&chip->wireless_insertion_work, |
| msecs_to_jiffies(DCIN_UNSUSPEND_DELAY_MS)); |
| if (chip->dc_psy_type != -EINVAL) |
| power_supply_set_online(&chip->dc_psy, |
| chip->dc_present); |
| |
| return 0; |
| } |
| /** |
| * dcin_uv_handler() - called when the dc voltage crosses the uv threshold |
| * @chip: pointer to smb135x_chg chip |
| * @rt_stat: the status bit indicating whether dc voltage is uv |
| */ |
| static int dcin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| /* |
| * rt_stat indicates if dc is undervolted. If so dc_present |
| * should be marked removed |
| */ |
| bool dc_present = !rt_stat; |
| |
| pr_debug("chip->dc_present = %d dc_present = %d\n", |
| chip->dc_present, dc_present); |
| |
| if (chip->dc_present && !dc_present) { |
| /* dc removed */ |
| chip->dc_present = dc_present; |
| handle_dc_removal(chip); |
| } |
| |
| if (!chip->dc_present && dc_present) { |
| /* dc inserted */ |
| chip->dc_present = dc_present; |
| handle_dc_insertion(chip); |
| } |
| |
| return 0; |
| } |
| |
| static int dcin_ov_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| /* |
| * rt_stat indicates if dc is overvolted. If so dc_present |
| * should be marked removed |
| */ |
| bool dc_present = !rt_stat; |
| |
| pr_debug("chip->dc_present = %d dc_present = %d\n", |
| chip->dc_present, dc_present); |
| |
| chip->dc_ov = !!rt_stat; |
| |
| if (chip->dc_present && !dc_present) { |
| /* dc removed */ |
| chip->dc_present = dc_present; |
| handle_dc_removal(chip); |
| } |
| |
| if (!chip->dc_present && dc_present) { |
| /* dc inserted */ |
| chip->dc_present = dc_present; |
| handle_dc_insertion(chip); |
| } |
| return 0; |
| } |
| |
| static int handle_usb_removal(struct smb135x_chg *chip) |
| { |
| if (chip->usb_psy) { |
| pr_debug("setting usb psy type = %d\n", |
| POWER_SUPPLY_TYPE_UNKNOWN); |
| power_supply_set_supply_type(chip->usb_psy, |
| POWER_SUPPLY_TYPE_UNKNOWN); |
| pr_debug("setting usb psy present = %d\n", chip->usb_present); |
| power_supply_set_present(chip->usb_psy, chip->usb_present); |
| pr_debug("setting usb psy allow detection 0\n"); |
| power_supply_set_allow_detection(chip->usb_psy, 0); |
| } |
| return 0; |
| } |
| |
| static int rerun_apsd(struct smb135x_chg *chip) |
| { |
| int rc; |
| |
| pr_debug("Reruning APSD\nDisabling APSD\n"); |
| rc = smb135x_masked_write(chip, CFG_11_REG, AUTO_SRC_DET_EN_BIT, 0); |
| if (rc) { |
| dev_err(chip->dev, "Couldn't Disable APSD rc=%d\n", rc); |
| return rc; |
| } |
| pr_debug("Allow only 9V chargers\n"); |
| rc = smb135x_masked_write(chip, CFG_C_REG, |
| USBIN_ADAPTER_ALLOWANCE_MASK, ALLOW_9V_ONLY); |
| if (rc) |
| dev_err(chip->dev, "Couldn't Allow 9V rc=%d\n", rc); |
| pr_debug("Enabling APSD\n"); |
| rc = smb135x_masked_write(chip, CFG_11_REG, AUTO_SRC_DET_EN_BIT, 1); |
| if (rc) |
| dev_err(chip->dev, "Couldn't Enable APSD rc=%d\n", rc); |
| pr_debug("Allow 5V-9V\n"); |
| rc = smb135x_masked_write(chip, CFG_C_REG, |
| USBIN_ADAPTER_ALLOWANCE_MASK, ALLOW_5V_TO_9V); |
| if (rc) |
| dev_err(chip->dev, "Couldn't Allow 5V-9V rc=%d\n", rc); |
| return rc; |
| } |
| |
| static int handle_usb_insertion(struct smb135x_chg *chip) |
| { |
| u8 reg; |
| int rc; |
| char *usb_type_name = "null"; |
| enum power_supply_type usb_supply_type; |
| |
| /* usb inserted */ |
| rc = smb135x_read(chip, STATUS_5_REG, ®); |
| if (rc < 0) { |
| dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); |
| return rc; |
| } |
| /* |
| * Report the charger type as UNKNOWN if the |
| * apsd-fail flag is set. This nofifies the USB driver |
| * to initiate a s/w based charger type detection. |
| */ |
| if (chip->workaround_flags & WRKARND_APSD_FAIL) |
| reg = 0; |
| |
| usb_type_name = get_usb_type_name(reg); |
| usb_supply_type = get_usb_supply_type(reg); |
| pr_debug("inserted %s, usb psy type = %d stat_5 = 0x%02x apsd_rerun = %d\n", |
| usb_type_name, usb_supply_type, reg, chip->apsd_rerun); |
| |
| if (chip->batt_present && !chip->apsd_rerun && chip->usb_psy) { |
| if (usb_supply_type == POWER_SUPPLY_TYPE_USB) { |
| pr_debug("setting usb psy allow detection 1 SDP and rerun\n"); |
| power_supply_set_allow_detection(chip->usb_psy, 1); |
| chip->apsd_rerun = true; |
| rerun_apsd(chip); |
| /* rising edge of src detect will happen in few mS */ |
| return 0; |
| } else { |
| pr_debug("setting usb psy allow detection 1 DCP and no rerun\n"); |
| power_supply_set_allow_detection(chip->usb_psy, 1); |
| } |
| } |
| |
| if (chip->usb_psy) { |
| if (chip->bms_controlled_charging) { |
| /* enable charging on USB insertion */ |
| rc = smb135x_charging_enable(chip, true); |
| if (rc < 0) |
| dev_err(chip->dev, "Couldn't enable charging rc = %d\n", |
| rc); |
| } |
| pr_debug("setting usb psy type = %d\n", usb_supply_type); |
| power_supply_set_supply_type(chip->usb_psy, usb_supply_type); |
| pr_debug("setting usb psy present = %d\n", chip->usb_present); |
| power_supply_set_present(chip->usb_psy, chip->usb_present); |
| } |
| chip->apsd_rerun = false; |
| return 0; |
| } |
| |
| /** |
| * usbin_uv_handler() - It is called for DCP charger removal |
| * @chip: pointer to smb135x_chg chip |
| * @rt_stat: the status bit indicating chg insertion/removal |
| */ |
| static int usbin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| /* |
| * rt_stat indicates if usb is undervolted. If so usb_present |
| * should be marked removed |
| */ |
| bool usb_present = !rt_stat; |
| union power_supply_propval prop = {0, }; |
| |
| pr_debug("chip->usb_present = %d usb_present = %d\n", |
| chip->usb_present, usb_present); |
| if (chip->usb_psy && !chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_TYPE, &prop)) { |
| if (prop.intval == POWER_SUPPLY_TYPE_USB_DCP) { |
| if (chip->usb_present && !usb_present) { |
| /* For DCP and HVDCP removing */ |
| chip->usb_present = usb_present; |
| handle_usb_removal(chip); |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int usbin_ov_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| /* |
| * rt_stat indicates if usb is overvolted. If so usb_present |
| * should be marked removed |
| */ |
| bool usb_present = !rt_stat; |
| int health; |
| |
| 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; |
| handle_usb_removal(chip); |
| } else if (!chip->usb_present && usb_present) { |
| /* USB inserted */ |
| chip->usb_present = usb_present; |
| handle_usb_insertion(chip); |
| } |
| |
| if (chip->usb_psy) { |
| health = rt_stat ? POWER_SUPPLY_HEALTH_OVERVOLTAGE |
| : POWER_SUPPLY_HEALTH_GOOD; |
| power_supply_set_health_state(chip->usb_psy, health); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * src_detect_handler() - this is called on rising edge when USB |
| * charger type is detected and on falling edge when |
| * USB voltage falls below the coarse detect voltage |
| * (1V), use it for handling USB charger insertion |
| * and CDP or SDP removal. |
| * @chip: pointer to smb135x_chg chip |
| * @rt_stat: the status bit indicating chg insertion/removal |
| */ |
| static int src_detect_handler(struct smb135x_chg *chip, u8 rt_stat) |
| { |
| bool usb_present = !!rt_stat; |
| union power_supply_propval prop = {0, }; |
| |
| pr_debug("chip->usb_present = %d usb_present = %d\n", |
| chip->usb_present, usb_present); |
| |
| if (!chip->usb_present && usb_present) { |
| /* USB inserted */ |
| chip->usb_present = usb_present; |
| handle_usb_insertion(chip); |
| } else if (usb_present && chip->apsd_rerun) { |
| handle_usb_insertion(chip); |
| } else if (chip->usb_psy && !chip->usb_psy->get_property( |
| chip->usb_psy, POWER_SUPPLY_PROP_TYPE, |
| &prop)) { |
| if (((prop.intval == POWER_SUPPLY_TYPE_USB_CDP) || |
| (prop.intval == POWER_SUPPLY_TYPE_USB)) && |
| chip->usb_present && !usb_present) { |
| /* CDP or SDP removed */ |
| chip->usb_present = !chip->usb_present; |
| handle_usb_removal(chip); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int chg_inhibit_handler(struct smb135x_chg *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); |
| |
| if (!chip->inhibit_disabled) |
| chip->chg_done_batt_full = !!rt_stat; |
| return 0; |
| } |
| |
| struct smb_irq_info { |
| const char *name; |
| int (*smb_irq)(struct smb135x_chg *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", |
| .smb_irq = taper_handler, |
| }, |
| { |
| .name = "recharge", |
| .smb_irq = recharge_handler, |
| }, |
| { |
| .name = "fast_chg", |
| .smb_irq = fast_chg_handler, |
| }, |
| }, |
| }, |
| {IRQ_D_REG, 0, 0, |
| { |
| { |
| .name = "prechg_timeout", |
| }, |
| { |
| .name = "safety_timeout", |
| .smb_irq = safety_timeout_handler, |
| }, |
| { |
| .name = "aicl_done", |
| }, |
| { |
| .name = "battery_ov", |
| }, |
| }, |
| }, |
| {IRQ_E_REG, 0, 0, |
| { |
| { |
| .name = "usbin_uv", |
| .smb_irq = usbin_uv_handler, |
| }, |
| { |
| .name = "usbin_ov", |
| .smb_irq = usbin_ov_handler, |
| }, |
| { |
| .name = "dcin_uv", |
| .smb_irq = dcin_uv_handler, |
| }, |
| { |
| .name = "dcin_ov", |
| .smb_irq = dcin_ov_handler, |
| }, |
| }, |
| }, |
| {IRQ_F_REG, 0, 0, |
| { |
| { |
| .name = "power_ok", |
| .smb_irq = power_ok_handler, |
| }, |
| { |
| .name = "rid", |
| .smb_irq = rid_handler, |
| }, |
| { |
| .name = "otg_fail", |
| }, |
| { |
| .name = "otg_oc", |
| .smb_irq = otg_oc_handler, |
| }, |
| }, |
| }, |
| {IRQ_G_REG, 0, 0, |
| { |
| { |
| .name = "chg_inhibit", |
| .smb_irq = chg_inhibit_handler, |
| }, |
| { |
| .name = "chg_error", |
| }, |
| { |
| .name = "wd_timeout", |
| }, |
| { |
| .name = "src_detect", |
| .smb_irq = src_detect_handler, |
| }, |
| }, |
| }, |
| }; |
| |
| static int smb135x_irq_read(struct smb135x_chg *chip) |
| { |
| int rc, i; |
| |
| /* |
| * When dcin path is suspended the irq triggered status is not cleared |
| * causing a storm. To prevent this situation unsuspend dcin path while |
| * reading interrupts and restore its status back. |
| */ |
| mutex_lock(&chip->path_suspend_lock); |
| |
| if (chip->dc_suspended) |
| __smb135x_dc_suspend(chip, false); |
| |
| for (i = 0; i < ARRAY_SIZE(handlers); i++) { |
| rc = smb135x_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); |
| handlers[i].val = 0; |
| continue; |
| } |
| } |
| |
| if (chip->dc_suspended) |
| __smb135x_dc_suspend(chip, true); |
| |
| mutex_unlock(&chip->path_suspend_lock); |
| |
| return rc; |
| } |
| #define IRQ_LATCHED_MASK 0x02 |
| #define IRQ_STATUS_MASK 0x01 |
| #define BITS_PER_IRQ 2 |
| static irqreturn_t smb135x_chg_stat_handler(int irq, void *dev_id) |
| { |
| struct smb135x_chg *chip = dev_id; |
| int i, j; |
| u8 triggered; |
| u8 changed; |
| u8 rt_stat, prev_rt_stat; |
| 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"); |
| disable_irq_nosync(irq); |
| mutex_unlock(&chip->irq_complete); |
| return IRQ_HANDLED; |
| } |
| chip->irq_waiting = false; |
| |
| smb135x_irq_read(chip); |
| for (i = 0; i < ARRAY_SIZE(handlers); i++) { |
| for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) { |
| 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) { |
| pr_debug("batt psy changed\n"); |
| power_supply_changed(&chip->batt_psy); |
| if (chip->usb_psy) { |
| pr_debug("usb psy changed\n"); |
| power_supply_changed(chip->usb_psy); |
| } |
| if (chip->dc_psy_type != -EINVAL) { |
| pr_debug("dc psy changed\n"); |
| power_supply_changed(&chip->dc_psy); |
| } |
| } |
| |
| mutex_unlock(&chip->irq_complete); |
| |
| return IRQ_HANDLED; |
| } |
| |
| #define LAST_CNFG_REG 0x1F |
| static int show_cnfg_regs(struct seq_file *m, void *data) |
| { |
| struct smb135x_chg *chip = m->private; |
| int rc; |
| u8 reg; |
| u8 addr; |
| |
| for (addr = 0; addr <= LAST_CNFG_REG; addr++) { |
| rc = smb135x_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 smb135x_chg *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 smb135x_chg *chip = m->private; |
| int rc; |
| u8 reg; |
| u8 addr; |
| |
| for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) { |
| rc = smb135x_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 smb135x_chg *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 0x46 |
| #define LAST_STATUS_REG 0x56 |
| static int show_status_regs(struct seq_file *m, void *data) |
| { |
| struct smb135x_chg *chip = m->private; |
| int rc; |
| u8 reg; |
| u8 addr; |
| |
| for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) { |
| rc = smb135x_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 smb135x_chg *chip = inode->i_private; |
| |
| return single_open(file, show_status_regs, chip); |
| } |
| |
| static const struct file_operations status_debugfs_ops = { |