android_kernel_samsung_msm8976/drivers/power/smb135x-charger.c

4500 lines
108 KiB
C
Raw Normal View History

/* 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,
};
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
bool parallel_charger;
bool parallel_charger_present;
bool bms_controlled_charging;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/* 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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
2013-11-05 18:46:37 +00:00
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;
2013-11-05 18:46:37 +00:00
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, &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, &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, &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, &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, &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, &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, &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, &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, &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(5, 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, &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;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
#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) {
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/* 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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
}
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,
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
};
static int smb135x_parallel_set_chg_present(struct smb135x_chg *chip,
int present)
{
u8 val;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
int rc;
if (present == chip->parallel_charger_present) {
pr_debug("present %d -> %d, skipping\n",
chip->parallel_charger_present, present);
return 0;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
if (present) {
/* 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;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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]);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/*
* 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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
}
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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, &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, &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,
};
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
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 dp=r dm=r\n");
power_supply_set_dp_dm(chip->usb_psy,
POWER_SUPPLY_DP_DM_DPR_DMR);
}
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, &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 dp=f dm=f SDP and rerun\n");
power_supply_set_dp_dm(chip->usb_psy,
POWER_SUPPLY_DP_DM_DPF_DMF);
chip->apsd_rerun = true;
rerun_apsd(chip);
/* rising edge of src detect will happen in few mS */
return 0;
} else {
pr_debug("Set usb psy dp=f dm=f DCP and no rerun\n");
power_supply_set_dp_dm(chip->usb_psy,
POWER_SUPPLY_DP_DM_DPF_DMF);
}
}
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;
2013-11-05 18:46:37 +00:00
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;
}
2013-11-05 18:46:37 +00:00
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);
}
}
2013-11-05 18:46:37 +00:00
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, &reg);
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, &reg);
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, &reg);
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 = {
.owner = THIS_MODULE,
.open = status_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
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++) {
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 smb135x_chg *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 smb135x_chg *chip = data;
int rc;
u8 temp;
rc = smb135x_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 smb135x_chg *chip = data;
int rc;
u8 temp;
temp = (u8) val;
rc = smb135x_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 force_irq_set(void *data, u64 val)
{
struct smb135x_chg *chip = data;
smb135x_chg_stat_handler(chip->client->irq, data);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(force_irq_ops, NULL, force_irq_set, "0x%02llx\n");
static int force_rechg_set(void *data, u64 val)
{
int rc = 0;
struct smb135x_chg *chip = data;
if (!chip->chg_enabled) {
pr_debug("Charging Disabled force recharge not allowed\n");
return -EINVAL;
}
if (!chip->inhibit_disabled) {
rc = smb135x_masked_write(chip, CFG_14_REG, EN_CHG_INHIBIT_BIT,
0);
if (rc)
dev_err(chip->dev,
"Couldn't disable charge-inhibit rc=%d\n", rc);
/* delay for charge-inhibit to take affect */
msleep(500);
}
rc |= smb135x_charging(chip, false);
rc |= smb135x_charging(chip, true);
if (!chip->inhibit_disabled) {
rc |= smb135x_masked_write(chip, CFG_14_REG,
EN_CHG_INHIBIT_BIT, EN_CHG_INHIBIT_BIT);
if (rc)
dev_err(chip->dev,
"Couldn't enable charge-inhibit rc=%d\n", rc);
}
return rc;
}
DEFINE_SIMPLE_ATTRIBUTE(force_rechg_ops, NULL, force_rechg_set, "0x%02llx\n");
#ifdef DEBUG
static void dump_regs(struct smb135x_chg *chip)
{
int rc;
u8 reg;
u8 addr;
for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
rc = smb135x_read(chip, addr, &reg);
if (rc < 0)
dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
addr, rc);
else
pr_debug("0x%02x = 0x%02x\n", addr, reg);
}
for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
rc = smb135x_read(chip, addr, &reg);
if (rc < 0)
dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
addr, rc);
else
pr_debug("0x%02x = 0x%02x\n", addr, reg);
}
for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
rc = smb135x_read(chip, addr, &reg);
if (rc < 0)
dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
addr, rc);
else
pr_debug("0x%02x = 0x%02x\n", addr, reg);
}
}
#else
static void dump_regs(struct smb135x_chg *chip)
{
}
#endif
static int determine_initial_status(struct smb135x_chg *chip)
{
int rc;
u8 reg;
/*
* It is okay to read the interrupt status here since
* interrupts aren't requested. reading interrupt status
* clears the interrupt so be careful to read interrupt
* status only in interrupt handling code
*/
chip->batt_present = true;
rc = smb135x_read(chip, IRQ_B_REG, &reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read irq b rc = %d\n", rc);
return rc;
}
if (reg & IRQ_B_BATT_TERMINAL_BIT || reg & IRQ_B_BATT_MISSING_BIT)
chip->batt_present = false;
rc = smb135x_read(chip, STATUS_4_REG, &reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read status 4 rc = %d\n", rc);
return rc;
}
/* treat battery gone if less than 2V */
if (reg & BATT_LESS_THAN_2V)
chip->batt_present = false;
rc = smb135x_read(chip, IRQ_A_REG, &reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
return rc;
}
if (reg & IRQ_A_HOT_HARD_BIT)
chip->batt_hot = true;
if (reg & IRQ_A_COLD_HARD_BIT)
chip->batt_cold = true;
if (reg & IRQ_A_HOT_SOFT_BIT)
chip->batt_warm = true;
if (reg & IRQ_A_COLD_SOFT_BIT)
chip->batt_cool = true;
rc = smb135x_read(chip, IRQ_C_REG, &reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
return rc;
}
if (reg & IRQ_C_TERM_BIT)
chip->chg_done_batt_full = true;
rc = smb135x_read(chip, IRQ_E_REG, &reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc);
return rc;
}
chip->usb_present = !(reg & IRQ_E_USB_OV_BIT)
&& !(reg & IRQ_E_USB_UV_BIT);
chip->dc_present = !(reg & IRQ_E_DC_OV_BIT) && !(reg & IRQ_E_DC_UV_BIT);
if (chip->usb_present)
handle_usb_insertion(chip);
else
handle_usb_removal(chip);
if (chip->dc_psy_type != -EINVAL) {
if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) {
/*
* put the dc path in suspend state if it is powered
* by wireless charger
*/
if (chip->dc_present)
smb135x_path_suspend(chip, DC, CURRENT, false);
else
smb135x_path_suspend(chip, DC, CURRENT, true);
}
}
chip->usb_slave_present = is_usb_slave_present(chip);
if (chip->usb_psy && !chip->id_line_not_connected) {
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;
}
static int smb135x_hw_init(struct smb135x_chg *chip)
{
int rc;
int i;
u8 reg, mask;
if (chip->pinctrl_state_name) {
chip->smb_pinctrl = pinctrl_get_select(chip->dev,
chip->pinctrl_state_name);
if (IS_ERR(chip->smb_pinctrl)) {
pr_err("Could not get/set %s pinctrl state rc = %ld\n",
chip->pinctrl_state_name,
PTR_ERR(chip->smb_pinctrl));
return PTR_ERR(chip->smb_pinctrl);
}
}
if (chip->therm_bias_vreg) {
rc = regulator_enable(chip->therm_bias_vreg);
if (rc) {
pr_err("Couldn't enable therm-bias rc = %d\n", rc);
return rc;
}
}
/*
* Enable USB data line pullup regulator this is needed for the D+
* line to be at proper voltage for HVDCP charger detection.
*/
if (chip->usb_pullup_vreg) {
rc = regulator_enable(chip->usb_pullup_vreg);
if (rc) {
pr_err("Unable to enable data line pull-up regulator rc=%d\n",
rc);
if (chip->therm_bias_vreg)
regulator_disable(chip->therm_bias_vreg);
return rc;
}
}
rc = smb135x_enable_volatile_writes(chip);
if (rc < 0) {
dev_err(chip->dev, "Couldn't configure for volatile rc = %d\n",
rc);
goto free_regulator;
}
/*
* force using current from the register i.e. ignore auto
* power source detect (APSD) mA ratings
*/
mask = USE_REGISTER_FOR_CURRENT;
if (chip->workaround_flags & WRKARND_USB100_BIT)
reg = 0;
else
/* this ignores APSD results */
reg = USE_REGISTER_FOR_CURRENT;
rc = smb135x_masked_write(chip, CMD_INPUT_LIMIT, mask, reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
goto free_regulator;
}
/* 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);
goto free_regulator;
}
/*
* set chg en by cmd register, set chg en by writing bit 1,
* enable auto pre to fast, enable current termination, enable
* auto recharge, enable chg inhibition based on the dt flag
*/
if (chip->inhibit_disabled)
reg = 0;
else
reg = EN_CHG_INHIBIT_BIT;
rc = smb135x_masked_write(chip, CFG_14_REG,
CHG_EN_BY_PIN_BIT | CHG_EN_ACTIVE_LOW_BIT
| PRE_TO_FAST_REQ_CMD_BIT | DISABLE_AUTO_RECHARGE_BIT
| EN_CHG_INHIBIT_BIT, reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set cfg 14 rc=%d\n", rc);
goto free_regulator;
}
/* 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);
/* 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);
goto free_regulator;
}
}
/* set iterm */
if (chip->iterm_ma != -EINVAL) {
if (chip->iterm_disabled) {
dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
rc = -EINVAL;
goto free_regulator;
} else {
if (chip->iterm_ma <= 50)
reg = CHG_ITERM_50MA;
else if (chip->iterm_ma <= 100)
reg = CHG_ITERM_100MA;
else if (chip->iterm_ma <= 150)
reg = CHG_ITERM_150MA;
else if (chip->iterm_ma <= 200)
reg = CHG_ITERM_200MA;
else if (chip->iterm_ma <= 250)
reg = CHG_ITERM_250MA;
else if (chip->iterm_ma <= 300)
reg = CHG_ITERM_300MA;
else if (chip->iterm_ma <= 500)
reg = CHG_ITERM_500MA;
else
reg = CHG_ITERM_600MA;
rc = smb135x_masked_write(chip, CFG_3_REG,
CHG_ITERM_MASK, reg);
if (rc) {
dev_err(chip->dev,
"Couldn't set iterm rc = %d\n", rc);
goto free_regulator;
}
rc = smb135x_masked_write(chip, CFG_14_REG,
DISABLE_CURRENT_TERM_BIT, 0);
if (rc) {
dev_err(chip->dev,
"Couldn't enable iterm rc = %d\n", rc);
goto free_regulator;
}
}
} else if (chip->iterm_disabled) {
rc = smb135x_masked_write(chip, CFG_14_REG,
DISABLE_CURRENT_TERM_BIT,
DISABLE_CURRENT_TERM_BIT);
if (rc) {
dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
rc);
goto free_regulator;
}
}
/* set the safety time voltage */
if (chip->safety_time != -EINVAL) {
if (chip->safety_time == 0) {
/* safety timer disabled */
reg = 1 << SAFETY_TIME_EN_SHIFT;
rc = smb135x_masked_write(chip, CFG_16_REG,
SAFETY_TIME_EN_BIT, reg);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't disable safety timer rc = %d\n",
rc);
goto free_regulator;
}
} 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 = smb135x_masked_write(chip, CFG_16_REG,
SAFETY_TIME_EN_BIT | SAFETY_TIME_MINUTES_MASK,
reg);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't set safety timer rc = %d\n",
rc);
goto free_regulator;
}
}
}
/* battery missing detection */
rc = smb135x_masked_write(chip, CFG_19_REG,
BATT_MISSING_ALGO_BIT | BATT_MISSING_THERM_BIT,
chip->bmd_algo_disabled ? BATT_MISSING_THERM_BIT :
BATT_MISSING_ALGO_BIT);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
rc);
goto free_regulator;
}
/* set maximum fastchg current */
if (chip->fastchg_ma != -EINVAL) {
rc = smb135x_set_fastchg_current(chip, chip->fastchg_ma);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set fastchg current = %d\n",
rc);
goto free_regulator;
}
}
if (chip->usb_pullup_vreg) {
/* enable 9V HVDCP adapter support */
rc = smb135x_masked_write(chip, CFG_E_REG, HVDCP_5_9_BIT,
HVDCP_5_9_BIT);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't request for 5 or 9V rc=%d\n", rc);
goto free_regulator;
}
}
if (chip->gamma_setting) {
rc = smb135x_masked_write(chip, CFG_1B_REG, COLD_HARD_MASK,
chip->gamma_setting[0] << COLD_HARD_SHIFT);
rc |= smb135x_masked_write(chip, CFG_1B_REG, HOT_HARD_MASK,
chip->gamma_setting[1] << HOT_HARD_SHIFT);
rc |= smb135x_masked_write(chip, CFG_1B_REG, COLD_SOFT_MASK,
chip->gamma_setting[2] << COLD_SOFT_SHIFT);
rc |= smb135x_masked_write(chip, CFG_1B_REG, HOT_SOFT_MASK,
chip->gamma_setting[3] << HOT_SOFT_SHIFT);
if (rc < 0)
goto free_regulator;
}
__smb135x_charging(chip, chip->chg_enabled);
/* interrupt enabling - active low */
if (chip->client->irq) {
mask = CHG_STAT_IRQ_ONLY_BIT | CHG_STAT_ACTIVE_HIGH_BIT
| CHG_STAT_DISABLE_BIT;
reg = CHG_STAT_IRQ_ONLY_BIT;
rc = smb135x_masked_write(chip, CFG_17_REG, mask, reg);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set irq config rc = %d\n",
rc);
goto free_regulator;
}
/* enabling only interesting interrupts */
rc = smb135x_write(chip, IRQ_CFG_REG,
IRQ_BAT_HOT_COLD_HARD_BIT
| IRQ_BAT_HOT_COLD_SOFT_BIT
| IRQ_OTG_OVER_CURRENT_BIT
| IRQ_INTERNAL_TEMPERATURE_BIT
| IRQ_USBIN_UV_BIT);
rc |= smb135x_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);
rc |= smb135x_write(chip, IRQ3_CFG_REG, IRQ3_SRC_DETECT_BIT
| IRQ3_DCIN_UV_BIT | IRQ3_RID_DETECT_BIT);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set irq enable rc = %d\n",
rc);
goto free_regulator;
}
}
/* resume threshold */
if (chip->resume_delta_mv != -EINVAL) {
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
smb135x_set_resume_threshold(chip, chip->resume_delta_mv);
}
/* DC path current settings */
if (chip->dc_psy_type != -EINVAL) {
rc = smb135x_set_dc_chg_current(chip, chip->dc_psy_ma);
if (rc < 0) {
dev_err(chip->dev, "Couldn't set dc charge current rc = %d\n",
rc);
goto free_regulator;
}
}
/*
* on some devices the battery is powered via external sources which
* could raise its voltage above the float voltage. smb135x chips go
* in to reverse boost in such a situation and the workaround is to
* disable float voltage compensation (note that the battery will appear
* hot/cold when powered via external source).
*/
if (chip->soft_vfloat_comp_disabled) {
mask = HOT_SOFT_VFLOAT_COMP_EN_BIT
| COLD_SOFT_VFLOAT_COMP_EN_BIT;
rc = smb135x_masked_write(chip, CFG_1A_REG, mask, 0);
if (rc < 0) {
dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n",
rc);
goto free_regulator;
}
}
if (chip->soft_current_comp_disabled) {
mask = HOT_SOFT_CURRENT_COMP_EN_BIT
| COLD_SOFT_CURRENT_COMP_EN_BIT;
rc = smb135x_masked_write(chip, CFG_1A_REG, mask, 0);
if (rc < 0) {
dev_err(chip->dev, "Couldn't disable soft current rc = %d\n",
rc);
goto free_regulator;
}
}
/*
* Command mode for OTG control. This gives us RID interrupts but keeps
* enabling the 5V OTG via i2c register control
*/
rc = smb135x_masked_write(chip, USBIN_OTG_REG, OTG_CNFG_MASK,
OTG_CNFG_COMMAND_CTRL);
if (rc < 0) {
dev_err(chip->dev, "Couldn't write to otg cfg reg rc = %d\n",
rc);
goto free_regulator;
}
return 0;
free_regulator:
if (chip->therm_bias_vreg)
regulator_disable(chip->therm_bias_vreg);
if (chip->usb_pullup_vreg)
regulator_disable(chip->usb_pullup_vreg);
return rc;
}
static struct of_device_id smb135x_match_table[] = {
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
{
.compatible = "qcom,smb1356-charger",
.data = &version_data[V_SMB1356],
},
{
.compatible = "qcom,smb1357-charger",
.data = &version_data[V_SMB1357],
},
{
.compatible = "qcom,smb1358-charger",
.data = &version_data[V_SMB1358],
},
{
.compatible = "qcom,smb1359-charger",
.data = &version_data[V_SMB1359],
},
{ },
};
#define DC_MA_MIN 300
#define DC_MA_MAX 2000
#define NUM_GAMMA_VALUES 4
static int smb_parse_dt(struct smb135x_chg *chip)
{
int rc;
struct device_node *node = chip->dev->of_node;
const char *dc_psy_type;
if (!node) {
dev_err(chip->dev, "device tree info. missing\n");
return -EINVAL;
}
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;
}
chip->bmd_algo_disabled = of_property_read_bool(node,
"qcom,bmd-algo-disabled");
chip->dc_psy_type = -EINVAL;
dc_psy_type = of_get_property(node, "qcom,dc-psy-type", NULL);
if (dc_psy_type) {
if (strcmp(dc_psy_type, "Mains") == 0)
chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS;
else if (strcmp(dc_psy_type, "Wireless") == 0)
chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS;
}
if (chip->dc_psy_type != -EINVAL) {
rc = of_property_read_u32(node, "qcom,dc-psy-ma",
&chip->dc_psy_ma);
if (rc < 0) {
dev_err(chip->dev,
"no mA current for dc rc = %d\n", rc);
return rc;
}
if (chip->dc_psy_ma < DC_MA_MIN
|| chip->dc_psy_ma > DC_MA_MAX) {
dev_err(chip->dev, "Bad dc mA %d\n", chip->dc_psy_ma);
return -EINVAL;
}
}
rc = of_property_read_u32(node, "qcom,recharge-thresh-mv",
&chip->resume_delta_mv);
if (rc < 0)
chip->resume_delta_mv = -EINVAL;
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_disabled_permanently = (of_property_read_bool(node,
"qcom,charging-disabled"));
chip->chg_enabled = !chip->chg_disabled_permanently;
chip->inhibit_disabled = of_property_read_bool(node,
"qcom,inhibit-disabled");
chip->bms_controlled_charging = of_property_read_bool(node,
"qcom,bms-controlled-charging");
rc = of_property_read_string(node, "qcom,bms-psy-name",
&chip->bms_psy_name);
if (rc)
chip->bms_psy_name = NULL;
rc = of_property_read_u32(node, "qcom,fastchg-ma", &chip->fastchg_ma);
if (rc < 0)
chip->fastchg_ma = -EINVAL;
chip->soft_vfloat_comp_disabled = of_property_read_bool(node,
"qcom,soft-vfloat-comp-disabled");
chip->soft_current_comp_disabled = of_property_read_bool(node,
"qcom,soft-current-comp-disabled");
if (of_find_property(node, "therm-bias-supply", NULL)) {
/* get the thermistor bias regulator */
chip->therm_bias_vreg = devm_regulator_get(chip->dev,
"therm-bias");
if (IS_ERR(chip->therm_bias_vreg))
return PTR_ERR(chip->therm_bias_vreg);
}
/*
* Gamma value indicates the ratio of the pull up resistors and NTC
* resistor in battery pack. There are 4 options, refer to the graphic
* user interface and choose the right one.
*/
if (of_find_property(node, "qcom,gamma-setting",
&chip->gamma_setting_num)) {
chip->gamma_setting_num = chip->gamma_setting_num /
sizeof(chip->gamma_setting_num);
if (NUM_GAMMA_VALUES != chip->gamma_setting_num) {
pr_err("Gamma setting not correct!\n");
return -EINVAL;
}
chip->gamma_setting = devm_kzalloc(chip->dev,
chip->gamma_setting_num *
sizeof(chip->gamma_setting_num), GFP_KERNEL);
if (!chip->gamma_setting) {
pr_err("gamma setting kzalloc failed!\n");
return -ENOMEM;
}
rc = of_property_read_u32_array(node,
"qcom,gamma-setting",
chip->gamma_setting, chip->gamma_setting_num);
if (rc) {
pr_err("Couldn't read gamma setting, 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;
}
}
if (of_find_property(node, "usb-pullup-supply", NULL)) {
/* get the data line pull-up regulator */
chip->usb_pullup_vreg = devm_regulator_get(chip->dev,
"usb-pullup");
if (IS_ERR(chip->usb_pullup_vreg))
return PTR_ERR(chip->usb_pullup_vreg);
}
chip->pinctrl_state_name = of_get_property(node, "pinctrl-names", NULL);
chip->id_line_not_connected = of_property_read_bool(node,
"qcom,id-line-not-connected");
return 0;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
static int create_debugfs_entries(struct smb135x_chg *chip)
{
chip->debug_root = debugfs_create_dir("smb135x", 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\n");
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\n");
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\n");
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\n");
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\n");
ent = debugfs_create_file("force_irq",
S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, chip,
&force_irq_ops);
if (!ent)
dev_err(chip->dev,
"Couldn't create force_irq debug file\n");
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 skip writes debug file\n");
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 skip reads debug file\n");
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 irq_count debug file\n");
ent = debugfs_create_file("force_recharge",
S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, chip,
&force_rechg_ops);
if (!ent)
dev_err(chip->dev,
"Couldn't create force recharge debug file\n");
ent = debugfs_create_x32("usb_suspend_votes",
S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root,
&(chip->usb_suspended));
if (!ent)
dev_err(chip->dev,
"Couldn't create usb_suspend_votes file\n");
ent = debugfs_create_x32("dc_suspend_votes",
S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root,
&(chip->dc_suspended));
if (!ent)
dev_err(chip->dev,
"Couldn't create dc_suspend_votes file\n");
}
return 0;
}
static int is_parallel_charger(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
return of_property_read_bool(node, "qcom,parallel-charger");
}
static int smb135x_main_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc;
struct smb135x_chg *chip;
struct power_supply *usb_psy;
u8 reg = 0;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) {
dev_err(&client->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
chip->client = client;
chip->dev = &client->dev;
rc = smb_parse_dt(chip);
if (rc < 0) {
dev_err(&client->dev, "Unable to parse DT nodes\n");
return rc;
}
usb_psy = power_supply_get_by_name("usb");
if (!usb_psy && chip->chg_enabled) {
dev_dbg(&client->dev, "USB supply not found; defer probe\n");
return -EPROBE_DEFER;
}
chip->usb_psy = usb_psy;
chip->fake_battery_soc = -EINVAL;
INIT_DELAYED_WORK(&chip->wireless_insertion_work,
wireless_insertion_work);
INIT_DELAYED_WORK(&chip->reset_otg_oc_count_work,
reset_otg_oc_count_work);
mutex_init(&chip->path_suspend_lock);
mutex_init(&chip->current_change_lock);
mutex_init(&chip->read_write_lock);
mutex_init(&chip->otg_oc_count_lock);
device_init_wakeup(chip->dev, true);
/* probe the device to check if its actually connected */
rc = smb135x_read(chip, CFG_4_REG, &reg);
if (rc) {
pr_err("Failed to detect SMB135x, device may be absent\n");
return -ENODEV;
}
i2c_set_clientdata(client, chip);
rc = smb135x_chip_version_and_revision(chip);
if (rc) {
dev_err(&client->dev,
"Couldn't detect version/revision rc=%d\n", rc);
return rc;
}
dump_regs(chip);
rc = smb135x_regulator_init(chip);
if (rc) {
dev_err(&client->dev,
"Couldn't initialize regulator rc=%d\n", rc);
return rc;
}
rc = smb135x_hw_init(chip);
if (rc < 0) {
dev_err(&client->dev,
"Unable to intialize hardware rc = %d\n", rc);
goto free_regulator;
}
rc = determine_initial_status(chip);
if (rc < 0) {
dev_err(&client->dev,
"Unable to determine init status rc = %d\n", rc);
goto free_regulator;
}
chip->batt_psy.name = "battery";
chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
chip->batt_psy.get_property = smb135x_battery_get_property;
chip->batt_psy.set_property = smb135x_battery_set_property;
chip->batt_psy.properties = smb135x_battery_properties;
chip->batt_psy.num_properties = ARRAY_SIZE(smb135x_battery_properties);
chip->batt_psy.external_power_changed = smb135x_external_power_changed;
chip->batt_psy.property_is_writeable = smb135x_battery_is_writeable;
if (chip->bms_controlled_charging) {
chip->batt_psy.supplied_to = pm_batt_supplied_to;
chip->batt_psy.num_supplicants =
ARRAY_SIZE(pm_batt_supplied_to);
}
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 free_regulator;
}
if (chip->dc_psy_type != -EINVAL) {
chip->dc_psy.name = "dc";
chip->dc_psy.type = chip->dc_psy_type;
chip->dc_psy.get_property = smb135x_dc_get_property;
chip->dc_psy.properties = smb135x_dc_properties;
chip->dc_psy.num_properties = ARRAY_SIZE(smb135x_dc_properties);
rc = power_supply_register(chip->dev, &chip->dc_psy);
if (rc < 0) {
dev_err(&client->dev,
"Unable to register dc_psy rc = %d\n", rc);
goto unregister_batt_psy;
}
}
chip->resume_completed = true;
2013-11-05 18:46:37 +00:00
mutex_init(&chip->irq_complete);
/* STAT irq configuration */
if (client->irq) {
rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
smb135x_chg_stat_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"smb135x_chg_stat_irq", chip);
if (rc < 0) {
dev_err(&client->dev,
"request_irq for irq=%d failed rc = %d\n",
client->irq, rc);
goto unregister_dc_psy;
}
enable_irq_wake(client->irq);
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
create_debugfs_entries(chip);
dev_info(chip->dev, "SMB135X version = %s revision = %s successfully probed batt=%d dc = %d usb = %d\n",
version_str[chip->version],
revision_str[chip->revision],
smb135x_get_prop_batt_present(chip),
chip->dc_present, chip->usb_present);
return 0;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
unregister_dc_psy:
if (chip->dc_psy_type != -EINVAL)
power_supply_unregister(&chip->dc_psy);
unregister_batt_psy:
power_supply_unregister(&chip->batt_psy);
free_regulator:
smb135x_regulator_deinit(chip);
return rc;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
static int smb135x_parallel_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc;
struct smb135x_chg *chip;
const struct of_device_id *match;
struct device_node *node = client->dev.of_node;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) {
dev_err(&client->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip->client = client;
chip->dev = &client->dev;
chip->parallel_charger = true;
chip->dc_psy_type = -EINVAL;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip->chg_enabled = !(of_property_read_bool(node,
"qcom,charging-disabled"));
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
rc = of_property_read_u32(node, "qcom,recharge-thresh-mv",
&chip->resume_delta_mv);
if (rc < 0)
chip->resume_delta_mv = -EINVAL;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
rc = of_property_read_u32(node, "qcom,float-voltage-mv",
&chip->vfloat_mv);
if (rc < 0)
chip->vfloat_mv = -EINVAL;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
mutex_init(&chip->path_suspend_lock);
mutex_init(&chip->current_change_lock);
mutex_init(&chip->read_write_lock);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
match = of_match_node(smb135x_match_table, node);
if (match == NULL) {
dev_err(chip->dev, "device tree match not found\n");
return -EINVAL;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip->version = *(int *)match->data;
smb135x_set_current_tables(chip);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
i2c_set_clientdata(client, chip);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip->parallel_psy.name = "usb-parallel";
chip->parallel_psy.type = POWER_SUPPLY_TYPE_USB_PARALLEL;
chip->parallel_psy.get_property = smb135x_parallel_get_property;
chip->parallel_psy.set_property = smb135x_parallel_set_property;
chip->parallel_psy.properties = smb135x_parallel_properties;
chip->parallel_psy.property_is_writeable
= smb135x_parallel_is_writeable;
chip->parallel_psy.num_properties
= ARRAY_SIZE(smb135x_parallel_properties);
rc = power_supply_register(chip->dev, &chip->parallel_psy);
if (rc < 0) {
dev_err(&client->dev,
"Unable to register parallel_psy rc = %d\n", rc);
return rc;
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
chip->resume_completed = true;
mutex_init(&chip->irq_complete);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
create_debugfs_entries(chip);
dev_info(chip->dev, "SMB135X USB PARALLEL CHARGER version = %s successfully probed\n",
version_str[chip->version]);
return 0;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
}
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
static int smb135x_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
if (is_parallel_charger(client))
return smb135x_parallel_charger_probe(client, id);
else
return smb135x_main_charger_probe(client, id);
}
static int smb135x_charger_remove(struct i2c_client *client)
{
int rc;
struct smb135x_chg *chip = i2c_get_clientdata(client);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
debugfs_remove_recursive(chip->debug_root);
if (chip->parallel_charger) {
power_supply_unregister(&chip->parallel_psy);
goto mutex_destroy;
}
if (chip->therm_bias_vreg) {
rc = regulator_disable(chip->therm_bias_vreg);
if (rc)
pr_err("Couldn't disable therm-bias rc = %d\n", rc);
}
if (chip->usb_pullup_vreg) {
rc = regulator_disable(chip->usb_pullup_vreg);
if (rc)
pr_err("Couldn't disable data-pullup rc = %d\n", rc);
}
if (chip->dc_psy_type != -EINVAL)
power_supply_unregister(&chip->dc_psy);
power_supply_unregister(&chip->batt_psy);
smb135x_regulator_deinit(chip);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
mutex_destroy:
mutex_destroy(&chip->irq_complete);
return 0;
}
static int smb135x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct smb135x_chg *chip = i2c_get_clientdata(client);
int i, rc;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/* no suspend resume activities for parallel charger */
if (chip->parallel_charger)
return 0;
/* Save the current IRQ config */
for (i = 0; i < 3; i++) {
rc = smb135x_read(chip, IRQ_CFG_REG + i,
&chip->irq_cfg_mask[i]);
if (rc)
dev_err(chip->dev,
"Couldn't save irq cfg regs rc=%d\n", rc);
}
/* enable only important IRQs */
rc = smb135x_write(chip, IRQ_CFG_REG, IRQ_USBIN_UV_BIT);
if (rc < 0)
dev_err(chip->dev, "Couldn't set irq_cfg rc = %d\n", rc);
rc = smb135x_write(chip, IRQ2_CFG_REG, IRQ2_BATT_MISSING_BIT
| IRQ2_VBAT_LOW_BIT
| IRQ2_POWER_OK_BIT);
if (rc < 0)
dev_err(chip->dev, "Couldn't set irq2_cfg rc = %d\n", rc);
rc = smb135x_write(chip, IRQ3_CFG_REG, IRQ3_SRC_DETECT_BIT
| IRQ3_DCIN_UV_BIT | IRQ3_RID_DETECT_BIT);
if (rc < 0)
dev_err(chip->dev, "Couldn't set irq3_cfg rc = %d\n", rc);
2013-11-05 18:46:37 +00:00
mutex_lock(&chip->irq_complete);
chip->resume_completed = false;
mutex_unlock(&chip->irq_complete);
return 0;
}
2013-11-05 18:46:37 +00:00
static int smb135x_suspend_noirq(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct smb135x_chg *chip = i2c_get_clientdata(client);
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/* no suspend resume activities for parallel charger */
if (chip->parallel_charger)
return 0;
2013-11-05 18:46:37 +00:00
if (chip->irq_waiting) {
pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
return -EBUSY;
}
return 0;
}
static int smb135x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct smb135x_chg *chip = i2c_get_clientdata(client);
int i, rc;
power: smb135x-charger: support parallel charging In this configuration the DC path is left unused. The D+ D- lines are not connected to smb135x and the output power only goes to the battery. This configuration is intended to assist the main charging path in constant current mode. The shutdown/suspend pin is connected to a signal which goes high when any charger is present. The chip is in shutdown state until no charger is present. The chip is in active state when a charger is inserted, however charging won't begin as the chip is configured to be in USB suspended state via pin control. The main charger is expected to enable smb135x when a strong USB charger is detected. The enable switches from pin control USB suspend to cmd control and disables USB suspend. Note that the fault output of the main charger is connected to the EN pin. This way charging stops on smb135x if the main charger sees a fault (hot/cold conditions). It is expected that when a USB charger is inserted the PRESENT property is set. The driver uses it to initialize registers. I2C access prior to this may not be available as the chip would be in the shutdown state. Also when charging has to begin, one has to set the MAX_CURRENT property with an appropriate value. This puts the USB path out of suspend and begins drawing current from it. Note that setting MAX_CURRENT = 2mA means USB host is suspending and the driver will suspend that path. When a USB charger is removed, it is expected that PRESENT property is unset. The order between setting of MAX_CURRENT=0 and setting PRESENT=0 doesn't matter. Change-Id: Ifb208f61d4ea2a380e3a900e4ed85316033cf6da Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
2014-05-30 19:04:55 +00:00
/* no suspend resume activities for parallel charger */
if (chip->parallel_charger)
return 0;
/* Restore the IRQ config */
for (i = 0; i < 3; i++) {
rc = smb135x_write(chip, IRQ_CFG_REG + i,
chip->irq_cfg_mask[i]);
if (rc)
dev_err(chip->dev,
"Couldn't restore irq cfg regs rc=%d\n", rc);
}
mutex_lock(&chip->irq_complete);
chip->resume_completed = true;
if (chip->irq_waiting) {
mutex_unlock(&chip->irq_complete);
smb135x_chg_stat_handler(client->irq, chip);
enable_irq(client->irq);
} else {
mutex_unlock(&chip->irq_complete);
}
return 0;
}
static const struct dev_pm_ops smb135x_pm_ops = {
.resume = smb135x_resume,
2013-11-05 18:46:37 +00:00
.suspend_noirq = smb135x_suspend_noirq,
.suspend = smb135x_suspend,
};
static const struct i2c_device_id smb135x_charger_id[] = {
{"smb135x-charger", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, smb135x_charger_id);
static void smb135x_shutdown(struct i2c_client *client)
{
int rc;
struct smb135x_chg *chip = i2c_get_clientdata(client);
if (chip->usb_pullup_vreg) {
/*
* switch to 5V adapter to prevent any errorneous request of 12V
* when USB D+ line pull-up regulator turns off.
*/
rc = smb135x_masked_write(chip, CFG_E_REG, HVDCP_5_9_BIT, 0);
if (rc < 0)
dev_err(chip->dev,
"Couldn't request for 5V rc=%d\n", rc);
}
}
static struct i2c_driver smb135x_charger_driver = {
.driver = {
.name = "smb135x-charger",
.owner = THIS_MODULE,
.of_match_table = smb135x_match_table,
.pm = &smb135x_pm_ops,
},
.probe = smb135x_charger_probe,
.remove = smb135x_charger_remove,
.id_table = smb135x_charger_id,
.shutdown = smb135x_shutdown,
};
module_i2c_driver(smb135x_charger_driver);
MODULE_DESCRIPTION("SMB135x Charger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("i2c:smb135x-charger");