mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1751 lines
49 KiB
1751 lines
49 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// BQ256XX Battery Charger Driver |
|
// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ |
|
|
|
#include <linux/err.h> |
|
#include <linux/i2c.h> |
|
#include <linux/init.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/gpio/consumer.h> |
|
#include <linux/power_supply.h> |
|
#include <linux/regmap.h> |
|
#include <linux/types.h> |
|
#include <linux/usb/phy.h> |
|
#include <linux/device.h> |
|
#include <linux/moduleparam.h> |
|
#include <linux/slab.h> |
|
#include <linux/acpi.h> |
|
|
|
#define BQ256XX_MANUFACTURER "Texas Instruments" |
|
|
|
#define BQ256XX_INPUT_CURRENT_LIMIT 0x00 |
|
#define BQ256XX_CHARGER_CONTROL_0 0x01 |
|
#define BQ256XX_CHARGE_CURRENT_LIMIT 0x02 |
|
#define BQ256XX_PRECHG_AND_TERM_CURR_LIM 0x03 |
|
#define BQ256XX_BATTERY_VOLTAGE_LIMIT 0x04 |
|
#define BQ256XX_CHARGER_CONTROL_1 0x05 |
|
#define BQ256XX_CHARGER_CONTROL_2 0x06 |
|
#define BQ256XX_CHARGER_CONTROL_3 0x07 |
|
#define BQ256XX_CHARGER_STATUS_0 0x08 |
|
#define BQ256XX_CHARGER_STATUS_1 0x09 |
|
#define BQ256XX_CHARGER_STATUS_2 0x0a |
|
#define BQ256XX_PART_INFORMATION 0x0b |
|
#define BQ256XX_CHARGER_CONTROL_4 0x0c |
|
|
|
#define BQ256XX_IINDPM_MASK GENMASK(4, 0) |
|
#define BQ256XX_IINDPM_STEP_uA 100000 |
|
#define BQ256XX_IINDPM_OFFSET_uA 100000 |
|
#define BQ256XX_IINDPM_MIN_uA 100000 |
|
#define BQ256XX_IINDPM_MAX_uA 3200000 |
|
#define BQ256XX_IINDPM_DEF_uA 2400000 |
|
|
|
#define BQ256XX_VINDPM_MASK GENMASK(3, 0) |
|
#define BQ256XX_VINDPM_STEP_uV 100000 |
|
#define BQ256XX_VINDPM_OFFSET_uV 3900000 |
|
#define BQ256XX_VINDPM_MIN_uV 3900000 |
|
#define BQ256XX_VINDPM_MAX_uV 5400000 |
|
#define BQ256XX_VINDPM_DEF_uV 4500000 |
|
|
|
#define BQ256XX_VBATREG_MASK GENMASK(7, 3) |
|
#define BQ2560X_VBATREG_STEP_uV 32000 |
|
#define BQ2560X_VBATREG_OFFSET_uV 3856000 |
|
#define BQ2560X_VBATREG_MIN_uV 3856000 |
|
#define BQ2560X_VBATREG_MAX_uV 4624000 |
|
#define BQ2560X_VBATREG_DEF_uV 4208000 |
|
#define BQ25601D_VBATREG_OFFSET_uV 3847000 |
|
#define BQ25601D_VBATREG_MIN_uV 3847000 |
|
#define BQ25601D_VBATREG_MAX_uV 4615000 |
|
#define BQ25601D_VBATREG_DEF_uV 4199000 |
|
#define BQ2561X_VBATREG_STEP_uV 10000 |
|
#define BQ25611D_VBATREG_MIN_uV 3494000 |
|
#define BQ25611D_VBATREG_MAX_uV 4510000 |
|
#define BQ25611D_VBATREG_DEF_uV 4190000 |
|
#define BQ25618_VBATREG_MIN_uV 3504000 |
|
#define BQ25618_VBATREG_MAX_uV 4500000 |
|
#define BQ25618_VBATREG_DEF_uV 4200000 |
|
#define BQ256XX_VBATREG_BIT_SHIFT 3 |
|
#define BQ2561X_VBATREG_THRESH 0x8 |
|
#define BQ25611D_VBATREG_THRESH_uV 4290000 |
|
#define BQ25618_VBATREG_THRESH_uV 4300000 |
|
|
|
#define BQ256XX_ITERM_MASK GENMASK(3, 0) |
|
#define BQ256XX_ITERM_STEP_uA 60000 |
|
#define BQ256XX_ITERM_OFFSET_uA 60000 |
|
#define BQ256XX_ITERM_MIN_uA 60000 |
|
#define BQ256XX_ITERM_MAX_uA 780000 |
|
#define BQ256XX_ITERM_DEF_uA 180000 |
|
#define BQ25618_ITERM_STEP_uA 20000 |
|
#define BQ25618_ITERM_OFFSET_uA 20000 |
|
#define BQ25618_ITERM_MIN_uA 20000 |
|
#define BQ25618_ITERM_MAX_uA 260000 |
|
#define BQ25618_ITERM_DEF_uA 60000 |
|
|
|
#define BQ256XX_IPRECHG_MASK GENMASK(7, 4) |
|
#define BQ256XX_IPRECHG_STEP_uA 60000 |
|
#define BQ256XX_IPRECHG_OFFSET_uA 60000 |
|
#define BQ256XX_IPRECHG_MIN_uA 60000 |
|
#define BQ256XX_IPRECHG_MAX_uA 780000 |
|
#define BQ256XX_IPRECHG_DEF_uA 180000 |
|
#define BQ25618_IPRECHG_STEP_uA 20000 |
|
#define BQ25618_IPRECHG_OFFSET_uA 20000 |
|
#define BQ25618_IPRECHG_MIN_uA 20000 |
|
#define BQ25618_IPRECHG_MAX_uA 260000 |
|
#define BQ25618_IPRECHG_DEF_uA 40000 |
|
#define BQ256XX_IPRECHG_BIT_SHIFT 4 |
|
|
|
#define BQ256XX_ICHG_MASK GENMASK(5, 0) |
|
#define BQ256XX_ICHG_STEP_uA 60000 |
|
#define BQ256XX_ICHG_MIN_uA 0 |
|
#define BQ256XX_ICHG_MAX_uA 3000000 |
|
#define BQ2560X_ICHG_DEF_uA 2040000 |
|
#define BQ25611D_ICHG_DEF_uA 1020000 |
|
#define BQ25618_ICHG_STEP_uA 20000 |
|
#define BQ25618_ICHG_MIN_uA 0 |
|
#define BQ25618_ICHG_MAX_uA 1500000 |
|
#define BQ25618_ICHG_DEF_uA 340000 |
|
#define BQ25618_ICHG_THRESH 0x3c |
|
#define BQ25618_ICHG_THRESH_uA 1180000 |
|
|
|
#define BQ256XX_VBUS_STAT_MASK GENMASK(7, 5) |
|
#define BQ256XX_VBUS_STAT_NO_INPUT 0 |
|
#define BQ256XX_VBUS_STAT_USB_SDP BIT(5) |
|
#define BQ256XX_VBUS_STAT_USB_CDP BIT(6) |
|
#define BQ256XX_VBUS_STAT_USB_DCP (BIT(6) | BIT(5)) |
|
#define BQ256XX_VBUS_STAT_USB_OTG (BIT(7) | BIT(6) | BIT(5)) |
|
|
|
#define BQ256XX_CHRG_STAT_MASK GENMASK(4, 3) |
|
#define BQ256XX_CHRG_STAT_NOT_CHRGING 0 |
|
#define BQ256XX_CHRG_STAT_PRECHRGING BIT(3) |
|
#define BQ256XX_CHRG_STAT_FAST_CHRGING BIT(4) |
|
#define BQ256XX_CHRG_STAT_CHRG_TERM (BIT(4) | BIT(3)) |
|
|
|
#define BQ256XX_PG_STAT_MASK BIT(2) |
|
#define BQ256XX_WDT_FAULT_MASK BIT(7) |
|
#define BQ256XX_CHRG_FAULT_MASK GENMASK(5, 4) |
|
#define BQ256XX_CHRG_FAULT_NORMAL 0 |
|
#define BQ256XX_CHRG_FAULT_INPUT BIT(4) |
|
#define BQ256XX_CHRG_FAULT_THERM BIT(5) |
|
#define BQ256XX_CHRG_FAULT_CST_EXPIRE (BIT(5) | BIT(4)) |
|
#define BQ256XX_BAT_FAULT_MASK BIT(3) |
|
#define BQ256XX_NTC_FAULT_MASK GENMASK(2, 0) |
|
#define BQ256XX_NTC_FAULT_WARM BIT(1) |
|
#define BQ256XX_NTC_FAULT_COOL (BIT(1) | BIT(0)) |
|
#define BQ256XX_NTC_FAULT_COLD (BIT(2) | BIT(0)) |
|
#define BQ256XX_NTC_FAULT_HOT (BIT(2) | BIT(1)) |
|
|
|
#define BQ256XX_NUM_WD_VAL 4 |
|
#define BQ256XX_WATCHDOG_MASK GENMASK(5, 4) |
|
#define BQ256XX_WATCHDOG_MAX 1600000 |
|
#define BQ256XX_WATCHDOG_DIS 0 |
|
#define BQ256XX_WDT_BIT_SHIFT 4 |
|
|
|
#define BQ256XX_REG_RST BIT(7) |
|
|
|
/** |
|
* struct bq256xx_init_data - |
|
* @ichg: fast charge current |
|
* @iindpm: input current limit |
|
* @vbatreg: charge voltage |
|
* @iterm: termination current |
|
* @iprechg: precharge current |
|
* @vindpm: input voltage limit |
|
* @ichg_max: maximum fast charge current |
|
* @vbatreg_max: maximum charge voltage |
|
*/ |
|
struct bq256xx_init_data { |
|
u32 ichg; |
|
u32 iindpm; |
|
u32 vbatreg; |
|
u32 iterm; |
|
u32 iprechg; |
|
u32 vindpm; |
|
u32 ichg_max; |
|
u32 vbatreg_max; |
|
}; |
|
|
|
/** |
|
* struct bq256xx_state - |
|
* @vbus_stat: VBUS status according to BQ256XX_CHARGER_STATUS_0 |
|
* @chrg_stat: charging status according to BQ256XX_CHARGER_STATUS_0 |
|
* @online: PG status according to BQ256XX_CHARGER_STATUS_0 |
|
* |
|
* @wdt_fault: watchdog fault according to BQ256XX_CHARGER_STATUS_1 |
|
* @bat_fault: battery fault according to BQ256XX_CHARGER_STATUS_1 |
|
* @chrg_fault: charging fault according to BQ256XX_CHARGER_STATUS_1 |
|
* @ntc_fault: TS fault according to BQ256XX_CHARGER_STATUS_1 |
|
*/ |
|
struct bq256xx_state { |
|
u8 vbus_stat; |
|
u8 chrg_stat; |
|
bool online; |
|
|
|
u8 wdt_fault; |
|
u8 bat_fault; |
|
u8 chrg_fault; |
|
u8 ntc_fault; |
|
}; |
|
|
|
enum bq256xx_id { |
|
BQ25600, |
|
BQ25600D, |
|
BQ25601, |
|
BQ25601D, |
|
BQ25618, |
|
BQ25619, |
|
BQ25611D, |
|
}; |
|
|
|
/** |
|
* struct bq256xx_device - |
|
* @client: i2c client structure |
|
* @regmap: register map structure |
|
* @dev: device structure |
|
* @charger: power supply registered for the charger |
|
* @battery: power supply registered for the battery |
|
* @lock: mutex lock structure |
|
* |
|
* @usb2_phy: usb_phy identifier |
|
* @usb3_phy: usb_phy identifier |
|
* @usb_nb: notifier block |
|
* @usb_work: usb work queue |
|
* @usb_event: usb_event code |
|
* |
|
* @model_name: i2c name string |
|
* |
|
* @init_data: initialization data |
|
* @chip_info: device variant information |
|
* @state: device status and faults |
|
* @watchdog_timer: watchdog timer value in milliseconds |
|
*/ |
|
struct bq256xx_device { |
|
struct i2c_client *client; |
|
struct device *dev; |
|
struct power_supply *charger; |
|
struct power_supply *battery; |
|
struct mutex lock; |
|
struct regmap *regmap; |
|
|
|
struct usb_phy *usb2_phy; |
|
struct usb_phy *usb3_phy; |
|
struct notifier_block usb_nb; |
|
struct work_struct usb_work; |
|
unsigned long usb_event; |
|
|
|
char model_name[I2C_NAME_SIZE]; |
|
|
|
struct bq256xx_init_data init_data; |
|
const struct bq256xx_chip_info *chip_info; |
|
struct bq256xx_state state; |
|
int watchdog_timer; |
|
}; |
|
|
|
/** |
|
* struct bq256xx_chip_info - |
|
* @model_id: device instance |
|
* |
|
* @bq256xx_regmap_config: regmap configuration struct |
|
* @bq256xx_get_ichg: pointer to instance specific get_ichg function |
|
* @bq256xx_get_iindpm: pointer to instance specific get_iindpm function |
|
* @bq256xx_get_vbatreg: pointer to instance specific get_vbatreg function |
|
* @bq256xx_get_iterm: pointer to instance specific get_iterm function |
|
* @bq256xx_get_iprechg: pointer to instance specific get_iprechg function |
|
* @bq256xx_get_vindpm: pointer to instance specific get_vindpm function |
|
* |
|
* @bq256xx_set_ichg: pointer to instance specific set_ichg function |
|
* @bq256xx_set_iindpm: pointer to instance specific set_iindpm function |
|
* @bq256xx_set_vbatreg: pointer to instance specific set_vbatreg function |
|
* @bq256xx_set_iterm: pointer to instance specific set_iterm function |
|
* @bq256xx_set_iprechg: pointer to instance specific set_iprechg function |
|
* @bq256xx_set_vindpm: pointer to instance specific set_vindpm function |
|
* |
|
* @bq256xx_def_ichg: default ichg value in microamps |
|
* @bq256xx_def_iindpm: default iindpm value in microamps |
|
* @bq256xx_def_vbatreg: default vbatreg value in microvolts |
|
* @bq256xx_def_iterm: default iterm value in microamps |
|
* @bq256xx_def_iprechg: default iprechg value in microamps |
|
* @bq256xx_def_vindpm: default vindpm value in microvolts |
|
* |
|
* @bq256xx_max_ichg: maximum charge current in microamps |
|
* @bq256xx_max_vbatreg: maximum battery regulation voltage in microvolts |
|
* |
|
* @has_usb_detect: indicates whether device has BC1.2 detection |
|
*/ |
|
struct bq256xx_chip_info { |
|
int model_id; |
|
|
|
const struct regmap_config *bq256xx_regmap_config; |
|
|
|
int (*bq256xx_get_ichg)(struct bq256xx_device *bq); |
|
int (*bq256xx_get_iindpm)(struct bq256xx_device *bq); |
|
int (*bq256xx_get_vbatreg)(struct bq256xx_device *bq); |
|
int (*bq256xx_get_iterm)(struct bq256xx_device *bq); |
|
int (*bq256xx_get_iprechg)(struct bq256xx_device *bq); |
|
int (*bq256xx_get_vindpm)(struct bq256xx_device *bq); |
|
|
|
int (*bq256xx_set_ichg)(struct bq256xx_device *bq, int ichg); |
|
int (*bq256xx_set_iindpm)(struct bq256xx_device *bq, int iindpm); |
|
int (*bq256xx_set_vbatreg)(struct bq256xx_device *bq, int vbatreg); |
|
int (*bq256xx_set_iterm)(struct bq256xx_device *bq, int iterm); |
|
int (*bq256xx_set_iprechg)(struct bq256xx_device *bq, int iprechg); |
|
int (*bq256xx_set_vindpm)(struct bq256xx_device *bq, int vindpm); |
|
|
|
int bq256xx_def_ichg; |
|
int bq256xx_def_iindpm; |
|
int bq256xx_def_vbatreg; |
|
int bq256xx_def_iterm; |
|
int bq256xx_def_iprechg; |
|
int bq256xx_def_vindpm; |
|
|
|
int bq256xx_max_ichg; |
|
int bq256xx_max_vbatreg; |
|
|
|
bool has_usb_detect; |
|
}; |
|
|
|
static int bq256xx_watchdog_time[BQ256XX_NUM_WD_VAL] = { |
|
0, 40000, 80000, 1600000 |
|
}; |
|
|
|
static const int bq25611d_vbatreg_values[] = { |
|
3494000, 3590000, 3686000, 3790000, 3894000, 3990000, 4090000, 4140000, |
|
4190000 |
|
}; |
|
|
|
static const int bq25618_619_vbatreg_values[] = { |
|
3504000, 3600000, 3696000, 3800000, 3904000, 4000000, 4100000, 4150000, |
|
4200000 |
|
}; |
|
|
|
static const int bq25618_619_ichg_values[] = { |
|
1290000, 1360000, 1430000, 1500000 |
|
}; |
|
|
|
static enum power_supply_usb_type bq256xx_usb_type[] = { |
|
POWER_SUPPLY_USB_TYPE_SDP, |
|
POWER_SUPPLY_USB_TYPE_CDP, |
|
POWER_SUPPLY_USB_TYPE_DCP, |
|
POWER_SUPPLY_USB_TYPE_UNKNOWN, |
|
POWER_SUPPLY_USB_TYPE_ACA, |
|
}; |
|
|
|
static int bq256xx_array_parse(int array_size, int val, const int array[]) |
|
{ |
|
int i = 0; |
|
|
|
if (val < array[i]) |
|
return i - 1; |
|
|
|
if (val >= array[array_size - 1]) |
|
return array_size - 1; |
|
|
|
for (i = 1; i < array_size; i++) { |
|
if (val == array[i]) |
|
return i; |
|
|
|
if (val > array[i - 1] && val < array[i]) { |
|
if (val < array[i]) |
|
return i - 1; |
|
else |
|
return i; |
|
} |
|
} |
|
return -EINVAL; |
|
} |
|
|
|
static int bq256xx_usb_notifier(struct notifier_block *nb, unsigned long val, |
|
void *priv) |
|
{ |
|
struct bq256xx_device *bq = |
|
container_of(nb, struct bq256xx_device, usb_nb); |
|
|
|
bq->usb_event = val; |
|
queue_work(system_power_efficient_wq, &bq->usb_work); |
|
|
|
return NOTIFY_OK; |
|
} |
|
|
|
static void bq256xx_usb_work(struct work_struct *data) |
|
{ |
|
struct bq256xx_device *bq = |
|
container_of(data, struct bq256xx_device, usb_work); |
|
|
|
switch (bq->usb_event) { |
|
case USB_EVENT_ID: |
|
break; |
|
case USB_EVENT_NONE: |
|
power_supply_changed(bq->charger); |
|
break; |
|
default: |
|
dev_err(bq->dev, "Error switching to charger mode.\n"); |
|
break; |
|
} |
|
} |
|
|
|
static struct reg_default bq2560x_reg_defs[] = { |
|
{BQ256XX_INPUT_CURRENT_LIMIT, 0x17}, |
|
{BQ256XX_CHARGER_CONTROL_0, 0x1a}, |
|
{BQ256XX_CHARGE_CURRENT_LIMIT, 0xa2}, |
|
{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x22}, |
|
{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x58}, |
|
{BQ256XX_CHARGER_CONTROL_1, 0x9f}, |
|
{BQ256XX_CHARGER_CONTROL_2, 0x66}, |
|
{BQ256XX_CHARGER_CONTROL_3, 0x4c}, |
|
}; |
|
|
|
static struct reg_default bq25611d_reg_defs[] = { |
|
{BQ256XX_INPUT_CURRENT_LIMIT, 0x17}, |
|
{BQ256XX_CHARGER_CONTROL_0, 0x1a}, |
|
{BQ256XX_CHARGE_CURRENT_LIMIT, 0x91}, |
|
{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x12}, |
|
{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x40}, |
|
{BQ256XX_CHARGER_CONTROL_1, 0x9e}, |
|
{BQ256XX_CHARGER_CONTROL_2, 0xe6}, |
|
{BQ256XX_CHARGER_CONTROL_3, 0x4c}, |
|
{BQ256XX_PART_INFORMATION, 0x54}, |
|
{BQ256XX_CHARGER_CONTROL_4, 0x75}, |
|
}; |
|
|
|
static struct reg_default bq25618_619_reg_defs[] = { |
|
{BQ256XX_INPUT_CURRENT_LIMIT, 0x17}, |
|
{BQ256XX_CHARGER_CONTROL_0, 0x1a}, |
|
{BQ256XX_CHARGE_CURRENT_LIMIT, 0x91}, |
|
{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x12}, |
|
{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x40}, |
|
{BQ256XX_CHARGER_CONTROL_1, 0x9e}, |
|
{BQ256XX_CHARGER_CONTROL_2, 0xe6}, |
|
{BQ256XX_CHARGER_CONTROL_3, 0x4c}, |
|
{BQ256XX_PART_INFORMATION, 0x2c}, |
|
{BQ256XX_CHARGER_CONTROL_4, 0x75}, |
|
}; |
|
|
|
static int bq256xx_get_state(struct bq256xx_device *bq, |
|
struct bq256xx_state *state) |
|
{ |
|
unsigned int charger_status_0; |
|
unsigned int charger_status_1; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_0, |
|
&charger_status_0); |
|
if (ret) |
|
return ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_1, |
|
&charger_status_1); |
|
if (ret) |
|
return ret; |
|
|
|
state->vbus_stat = charger_status_0 & BQ256XX_VBUS_STAT_MASK; |
|
state->chrg_stat = charger_status_0 & BQ256XX_CHRG_STAT_MASK; |
|
state->online = charger_status_0 & BQ256XX_PG_STAT_MASK; |
|
|
|
state->wdt_fault = charger_status_1 & BQ256XX_WDT_FAULT_MASK; |
|
state->bat_fault = charger_status_1 & BQ256XX_BAT_FAULT_MASK; |
|
state->chrg_fault = charger_status_1 & BQ256XX_CHRG_FAULT_MASK; |
|
state->ntc_fault = charger_status_1 & BQ256XX_NTC_FAULT_MASK; |
|
|
|
return 0; |
|
} |
|
|
|
static int bq256xx_get_ichg_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int charge_current_limit; |
|
unsigned int ichg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT, |
|
&charge_current_limit); |
|
if (ret) |
|
return ret; |
|
|
|
ichg_reg_code = charge_current_limit & BQ256XX_ICHG_MASK; |
|
|
|
return ichg_reg_code * BQ256XX_ICHG_STEP_uA; |
|
} |
|
|
|
static int bq25618_619_get_ichg_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int charge_current_limit; |
|
unsigned int ichg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT, |
|
&charge_current_limit); |
|
if (ret) |
|
return ret; |
|
|
|
ichg_reg_code = charge_current_limit & BQ256XX_ICHG_MASK; |
|
|
|
if (ichg_reg_code < BQ25618_ICHG_THRESH) |
|
return ichg_reg_code * BQ25618_ICHG_STEP_uA; |
|
|
|
return bq25618_619_ichg_values[ichg_reg_code - BQ25618_ICHG_THRESH]; |
|
} |
|
|
|
static int bq256xx_set_ichg_curr(struct bq256xx_device *bq, int ichg) |
|
{ |
|
unsigned int ichg_reg_code; |
|
int ichg_max = bq->init_data.ichg_max; |
|
|
|
ichg = clamp(ichg, BQ256XX_ICHG_MIN_uA, ichg_max); |
|
ichg_reg_code = ichg / BQ256XX_ICHG_STEP_uA; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT, |
|
BQ256XX_ICHG_MASK, ichg_reg_code); |
|
} |
|
|
|
static int bq25618_619_set_ichg_curr(struct bq256xx_device *bq, int ichg) |
|
{ |
|
int array_size = ARRAY_SIZE(bq25618_619_ichg_values); |
|
unsigned int ichg_reg_code; |
|
int ichg_max = bq->init_data.ichg_max; |
|
|
|
ichg = clamp(ichg, BQ25618_ICHG_MIN_uA, ichg_max); |
|
|
|
if (ichg <= BQ25618_ICHG_THRESH_uA) { |
|
ichg_reg_code = ichg / BQ25618_ICHG_STEP_uA; |
|
} else { |
|
ichg_reg_code = bq256xx_array_parse(array_size, ichg, |
|
bq25618_619_ichg_values) + BQ25618_ICHG_THRESH; |
|
} |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT, |
|
BQ256XX_ICHG_MASK, ichg_reg_code); |
|
} |
|
|
|
static int bq25618_619_get_chrg_volt(struct bq256xx_device *bq) |
|
{ |
|
unsigned int battery_volt_lim; |
|
unsigned int vbatreg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
&battery_volt_lim); |
|
|
|
if (ret) |
|
return ret; |
|
|
|
vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >> |
|
BQ256XX_VBATREG_BIT_SHIFT; |
|
|
|
if (vbatreg_reg_code > BQ2561X_VBATREG_THRESH) |
|
return ((vbatreg_reg_code - BQ2561X_VBATREG_THRESH) * |
|
BQ2561X_VBATREG_STEP_uV) + |
|
BQ25618_VBATREG_THRESH_uV; |
|
|
|
return bq25618_619_vbatreg_values[vbatreg_reg_code]; |
|
} |
|
|
|
static int bq25611d_get_chrg_volt(struct bq256xx_device *bq) |
|
{ |
|
unsigned int battery_volt_lim; |
|
unsigned int vbatreg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
&battery_volt_lim); |
|
if (ret) |
|
return ret; |
|
|
|
vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >> |
|
BQ256XX_VBATREG_BIT_SHIFT; |
|
|
|
if (vbatreg_reg_code > BQ2561X_VBATREG_THRESH) |
|
return ((vbatreg_reg_code - BQ2561X_VBATREG_THRESH) * |
|
BQ2561X_VBATREG_STEP_uV) + |
|
BQ25611D_VBATREG_THRESH_uV; |
|
|
|
return bq25611d_vbatreg_values[vbatreg_reg_code]; |
|
} |
|
|
|
static int bq2560x_get_chrg_volt(struct bq256xx_device *bq) |
|
{ |
|
unsigned int battery_volt_lim; |
|
unsigned int vbatreg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
&battery_volt_lim); |
|
if (ret) |
|
return ret; |
|
|
|
vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >> |
|
BQ256XX_VBATREG_BIT_SHIFT; |
|
|
|
return (vbatreg_reg_code * BQ2560X_VBATREG_STEP_uV) |
|
+ BQ2560X_VBATREG_OFFSET_uV; |
|
} |
|
|
|
static int bq25601d_get_chrg_volt(struct bq256xx_device *bq) |
|
{ |
|
unsigned int battery_volt_lim; |
|
unsigned int vbatreg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
&battery_volt_lim); |
|
if (ret) |
|
return ret; |
|
|
|
vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >> |
|
BQ256XX_VBATREG_BIT_SHIFT; |
|
|
|
return (vbatreg_reg_code * BQ2560X_VBATREG_STEP_uV) |
|
+ BQ25601D_VBATREG_OFFSET_uV; |
|
} |
|
|
|
static int bq25618_619_set_chrg_volt(struct bq256xx_device *bq, int vbatreg) |
|
{ |
|
int array_size = ARRAY_SIZE(bq25618_619_vbatreg_values); |
|
unsigned int vbatreg_reg_code; |
|
int vbatreg_max = bq->init_data.vbatreg_max; |
|
|
|
vbatreg = clamp(vbatreg, BQ25618_VBATREG_MIN_uV, vbatreg_max); |
|
|
|
if (vbatreg > BQ25618_VBATREG_THRESH_uV) |
|
vbatreg_reg_code = ((vbatreg - |
|
BQ25618_VBATREG_THRESH_uV) / |
|
(BQ2561X_VBATREG_STEP_uV)) + BQ2561X_VBATREG_THRESH; |
|
else { |
|
vbatreg_reg_code = bq256xx_array_parse(array_size, vbatreg, |
|
bq25618_619_vbatreg_values); |
|
} |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
BQ256XX_VBATREG_MASK, vbatreg_reg_code << |
|
BQ256XX_VBATREG_BIT_SHIFT); |
|
} |
|
|
|
static int bq25611d_set_chrg_volt(struct bq256xx_device *bq, int vbatreg) |
|
{ |
|
int array_size = ARRAY_SIZE(bq25611d_vbatreg_values); |
|
unsigned int vbatreg_reg_code; |
|
int vbatreg_max = bq->init_data.vbatreg_max; |
|
|
|
vbatreg = clamp(vbatreg, BQ25611D_VBATREG_MIN_uV, vbatreg_max); |
|
|
|
if (vbatreg > BQ25611D_VBATREG_THRESH_uV) |
|
vbatreg_reg_code = ((vbatreg - |
|
BQ25611D_VBATREG_THRESH_uV) / |
|
(BQ2561X_VBATREG_STEP_uV)) + BQ2561X_VBATREG_THRESH; |
|
else { |
|
vbatreg_reg_code = bq256xx_array_parse(array_size, vbatreg, |
|
bq25611d_vbatreg_values); |
|
} |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
BQ256XX_VBATREG_MASK, vbatreg_reg_code << |
|
BQ256XX_VBATREG_BIT_SHIFT); |
|
} |
|
|
|
static int bq2560x_set_chrg_volt(struct bq256xx_device *bq, int vbatreg) |
|
{ |
|
unsigned int vbatreg_reg_code; |
|
int vbatreg_max = bq->init_data.vbatreg_max; |
|
|
|
vbatreg = clamp(vbatreg, BQ2560X_VBATREG_MIN_uV, vbatreg_max); |
|
|
|
vbatreg_reg_code = (vbatreg - BQ2560X_VBATREG_OFFSET_uV) / |
|
BQ2560X_VBATREG_STEP_uV; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
BQ256XX_VBATREG_MASK, vbatreg_reg_code << |
|
BQ256XX_VBATREG_BIT_SHIFT); |
|
} |
|
|
|
static int bq25601d_set_chrg_volt(struct bq256xx_device *bq, int vbatreg) |
|
{ |
|
unsigned int vbatreg_reg_code; |
|
int vbatreg_max = bq->init_data.vbatreg_max; |
|
|
|
vbatreg = clamp(vbatreg, BQ25601D_VBATREG_MIN_uV, vbatreg_max); |
|
|
|
vbatreg_reg_code = (vbatreg - BQ25601D_VBATREG_OFFSET_uV) / |
|
BQ2560X_VBATREG_STEP_uV; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT, |
|
BQ256XX_VBATREG_MASK, vbatreg_reg_code << |
|
BQ256XX_VBATREG_BIT_SHIFT); |
|
} |
|
|
|
static int bq256xx_get_prechrg_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int prechg_and_term_curr_lim; |
|
unsigned int iprechg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
&prechg_and_term_curr_lim); |
|
if (ret) |
|
return ret; |
|
|
|
iprechg_reg_code = (prechg_and_term_curr_lim & BQ256XX_IPRECHG_MASK) |
|
>> BQ256XX_IPRECHG_BIT_SHIFT; |
|
|
|
return (iprechg_reg_code * BQ256XX_IPRECHG_STEP_uA) + |
|
BQ256XX_IPRECHG_OFFSET_uA; |
|
} |
|
|
|
static int bq256xx_set_prechrg_curr(struct bq256xx_device *bq, int iprechg) |
|
{ |
|
unsigned int iprechg_reg_code; |
|
|
|
iprechg = clamp(iprechg, BQ256XX_IPRECHG_MIN_uA, |
|
BQ256XX_IPRECHG_MAX_uA); |
|
|
|
iprechg_reg_code = ((iprechg - BQ256XX_IPRECHG_OFFSET_uA) / |
|
BQ256XX_IPRECHG_STEP_uA) << BQ256XX_IPRECHG_BIT_SHIFT; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
BQ256XX_IPRECHG_MASK, iprechg_reg_code); |
|
} |
|
|
|
static int bq25618_619_get_prechrg_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int prechg_and_term_curr_lim; |
|
unsigned int iprechg_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
&prechg_and_term_curr_lim); |
|
if (ret) |
|
return ret; |
|
|
|
iprechg_reg_code = (prechg_and_term_curr_lim & BQ256XX_IPRECHG_MASK) |
|
>> BQ256XX_IPRECHG_BIT_SHIFT; |
|
|
|
return (iprechg_reg_code * BQ25618_IPRECHG_STEP_uA) + |
|
BQ25618_IPRECHG_OFFSET_uA; |
|
} |
|
|
|
static int bq25618_619_set_prechrg_curr(struct bq256xx_device *bq, int iprechg) |
|
{ |
|
unsigned int iprechg_reg_code; |
|
|
|
iprechg = clamp(iprechg, BQ25618_IPRECHG_MIN_uA, |
|
BQ25618_IPRECHG_MAX_uA); |
|
|
|
iprechg_reg_code = ((iprechg - BQ25618_IPRECHG_OFFSET_uA) / |
|
BQ25618_IPRECHG_STEP_uA) << BQ256XX_IPRECHG_BIT_SHIFT; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
BQ256XX_IPRECHG_MASK, iprechg_reg_code); |
|
} |
|
|
|
static int bq256xx_get_term_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int prechg_and_term_curr_lim; |
|
unsigned int iterm_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
&prechg_and_term_curr_lim); |
|
if (ret) |
|
return ret; |
|
|
|
iterm_reg_code = prechg_and_term_curr_lim & BQ256XX_ITERM_MASK; |
|
|
|
return (iterm_reg_code * BQ256XX_ITERM_STEP_uA) + |
|
BQ256XX_ITERM_OFFSET_uA; |
|
} |
|
|
|
static int bq256xx_set_term_curr(struct bq256xx_device *bq, int iterm) |
|
{ |
|
unsigned int iterm_reg_code; |
|
|
|
iterm = clamp(iterm, BQ256XX_ITERM_MIN_uA, BQ256XX_ITERM_MAX_uA); |
|
|
|
iterm_reg_code = (iterm - BQ256XX_ITERM_OFFSET_uA) / |
|
BQ256XX_ITERM_STEP_uA; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
BQ256XX_ITERM_MASK, iterm_reg_code); |
|
} |
|
|
|
static int bq25618_619_get_term_curr(struct bq256xx_device *bq) |
|
{ |
|
unsigned int prechg_and_term_curr_lim; |
|
unsigned int iterm_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
&prechg_and_term_curr_lim); |
|
if (ret) |
|
return ret; |
|
|
|
iterm_reg_code = prechg_and_term_curr_lim & BQ256XX_ITERM_MASK; |
|
|
|
return (iterm_reg_code * BQ25618_ITERM_STEP_uA) + |
|
BQ25618_ITERM_OFFSET_uA; |
|
} |
|
|
|
static int bq25618_619_set_term_curr(struct bq256xx_device *bq, int iterm) |
|
{ |
|
unsigned int iterm_reg_code; |
|
|
|
iterm = clamp(iterm, BQ25618_ITERM_MIN_uA, BQ25618_ITERM_MAX_uA); |
|
|
|
iterm_reg_code = (iterm - BQ25618_ITERM_OFFSET_uA) / |
|
BQ25618_ITERM_STEP_uA; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM, |
|
BQ256XX_ITERM_MASK, iterm_reg_code); |
|
} |
|
|
|
static int bq256xx_get_input_volt_lim(struct bq256xx_device *bq) |
|
{ |
|
unsigned int charger_control_2; |
|
unsigned int vindpm_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_CHARGER_CONTROL_2, |
|
&charger_control_2); |
|
if (ret) |
|
return ret; |
|
|
|
vindpm_reg_code = charger_control_2 & BQ256XX_VINDPM_MASK; |
|
|
|
return (vindpm_reg_code * BQ256XX_VINDPM_STEP_uV) + |
|
BQ256XX_VINDPM_OFFSET_uV; |
|
} |
|
|
|
static int bq256xx_set_input_volt_lim(struct bq256xx_device *bq, int vindpm) |
|
{ |
|
unsigned int vindpm_reg_code; |
|
|
|
vindpm = clamp(vindpm, BQ256XX_VINDPM_MIN_uV, BQ256XX_VINDPM_MAX_uV); |
|
|
|
vindpm_reg_code = (vindpm - BQ256XX_VINDPM_OFFSET_uV) / |
|
BQ256XX_VINDPM_STEP_uV; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_CHARGER_CONTROL_2, |
|
BQ256XX_VINDPM_MASK, vindpm_reg_code); |
|
} |
|
|
|
static int bq256xx_get_input_curr_lim(struct bq256xx_device *bq) |
|
{ |
|
unsigned int input_current_limit; |
|
unsigned int iindpm_reg_code; |
|
int ret; |
|
|
|
ret = regmap_read(bq->regmap, BQ256XX_INPUT_CURRENT_LIMIT, |
|
&input_current_limit); |
|
if (ret) |
|
return ret; |
|
|
|
iindpm_reg_code = input_current_limit & BQ256XX_IINDPM_MASK; |
|
|
|
return (iindpm_reg_code * BQ256XX_IINDPM_STEP_uA) + |
|
BQ256XX_IINDPM_OFFSET_uA; |
|
} |
|
|
|
static int bq256xx_set_input_curr_lim(struct bq256xx_device *bq, int iindpm) |
|
{ |
|
unsigned int iindpm_reg_code; |
|
|
|
iindpm = clamp(iindpm, BQ256XX_IINDPM_MIN_uA, BQ256XX_IINDPM_MAX_uA); |
|
|
|
iindpm_reg_code = (iindpm - BQ256XX_IINDPM_OFFSET_uA) / |
|
BQ256XX_IINDPM_STEP_uA; |
|
|
|
return regmap_update_bits(bq->regmap, BQ256XX_INPUT_CURRENT_LIMIT, |
|
BQ256XX_IINDPM_MASK, iindpm_reg_code); |
|
} |
|
|
|
static void bq256xx_charger_reset(void *data) |
|
{ |
|
struct bq256xx_device *bq = data; |
|
|
|
regmap_update_bits(bq->regmap, BQ256XX_PART_INFORMATION, |
|
BQ256XX_REG_RST, BQ256XX_REG_RST); |
|
|
|
if (!IS_ERR_OR_NULL(bq->usb2_phy)) |
|
usb_unregister_notifier(bq->usb2_phy, &bq->usb_nb); |
|
|
|
if (!IS_ERR_OR_NULL(bq->usb3_phy)) |
|
usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb); |
|
} |
|
|
|
static int bq256xx_set_charger_property(struct power_supply *psy, |
|
enum power_supply_property prop, |
|
const union power_supply_propval *val) |
|
{ |
|
struct bq256xx_device *bq = power_supply_get_drvdata(psy); |
|
int ret = -EINVAL; |
|
|
|
switch (prop) { |
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
|
ret = bq->chip_info->bq256xx_set_iindpm(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_STATUS: |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
|
ret = bq->chip_info->bq256xx_set_vbatreg(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
|
ret = bq->chip_info->bq256xx_set_ichg(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: |
|
ret = bq->chip_info->bq256xx_set_iprechg(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: |
|
ret = bq->chip_info->bq256xx_set_iterm(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
|
ret = bq->chip_info->bq256xx_set_vindpm(bq, val->intval); |
|
if (ret) |
|
return ret; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
|
|
static int bq256xx_get_battery_property(struct power_supply *psy, |
|
enum power_supply_property psp, |
|
union power_supply_propval *val) |
|
{ |
|
struct bq256xx_device *bq = power_supply_get_drvdata(psy); |
|
|
|
switch (psp) { |
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
|
val->intval = bq->init_data.ichg_max; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: |
|
val->intval = bq->init_data.vbatreg_max; |
|
break; |
|
|
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int bq256xx_get_charger_property(struct power_supply *psy, |
|
enum power_supply_property psp, |
|
union power_supply_propval *val) |
|
{ |
|
struct bq256xx_device *bq = power_supply_get_drvdata(psy); |
|
struct bq256xx_state state; |
|
int ret = 0; |
|
|
|
mutex_lock(&bq->lock); |
|
ret = bq256xx_get_state(bq, &state); |
|
mutex_unlock(&bq->lock); |
|
if (ret) |
|
return ret; |
|
|
|
switch (psp) { |
|
case POWER_SUPPLY_PROP_STATUS: |
|
if (state.vbus_stat == BQ256XX_VBUS_STAT_NO_INPUT || |
|
state.vbus_stat == BQ256XX_VBUS_STAT_USB_OTG) |
|
val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
|
else if (state.chrg_stat == BQ256XX_CHRG_STAT_NOT_CHRGING) |
|
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
|
else if (state.chrg_stat == BQ256XX_CHRG_STAT_CHRG_TERM) |
|
val->intval = POWER_SUPPLY_STATUS_FULL; |
|
else |
|
val->intval = POWER_SUPPLY_STATUS_CHARGING; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_HEALTH: |
|
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; |
|
if (state.wdt_fault) { |
|
val->intval = |
|
POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE; |
|
} else if (state.bat_fault) { |
|
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
|
} else { |
|
switch (state.chrg_stat) { |
|
case BQ256XX_CHRG_FAULT_INPUT: |
|
val->intval = |
|
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
|
break; |
|
case BQ256XX_CHRG_FAULT_THERM: |
|
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; |
|
break; |
|
case BQ256XX_CHRG_FAULT_CST_EXPIRE: |
|
val->intval = |
|
POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
switch (state.ntc_fault) { |
|
case BQ256XX_NTC_FAULT_WARM: |
|
val->intval = POWER_SUPPLY_HEALTH_WARM; |
|
break; |
|
case BQ256XX_NTC_FAULT_COOL: |
|
val->intval = POWER_SUPPLY_HEALTH_COOL; |
|
break; |
|
case BQ256XX_NTC_FAULT_COLD: |
|
val->intval = POWER_SUPPLY_HEALTH_COLD; |
|
break; |
|
case BQ256XX_NTC_FAULT_HOT: |
|
val->intval = POWER_SUPPLY_HEALTH_HOT; |
|
break; |
|
default: |
|
val->intval = POWER_SUPPLY_HEALTH_GOOD; |
|
break; |
|
} |
|
} |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_USB_TYPE: |
|
if (bq->chip_info->has_usb_detect) { |
|
switch (state.vbus_stat) { |
|
case BQ256XX_VBUS_STAT_USB_SDP: |
|
val->intval = POWER_SUPPLY_USB_TYPE_SDP; |
|
break; |
|
case BQ256XX_VBUS_STAT_USB_CDP: |
|
val->intval = POWER_SUPPLY_USB_TYPE_CDP; |
|
break; |
|
case BQ256XX_VBUS_STAT_USB_DCP: |
|
val->intval = POWER_SUPPLY_USB_TYPE_DCP; |
|
break; |
|
case BQ256XX_VBUS_STAT_USB_OTG: |
|
val->intval = POWER_SUPPLY_USB_TYPE_ACA; |
|
break; |
|
default: |
|
val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; |
|
break; |
|
} |
|
} else { |
|
switch (state.vbus_stat) { |
|
case BQ256XX_VBUS_STAT_USB_SDP: |
|
val->intval = POWER_SUPPLY_USB_TYPE_SDP; |
|
break; |
|
case BQ256XX_VBUS_STAT_USB_OTG: |
|
val->intval = POWER_SUPPLY_USB_TYPE_ACA; |
|
break; |
|
default: |
|
val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; |
|
break; |
|
} |
|
} |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE: |
|
switch (state.chrg_stat) { |
|
case BQ256XX_CHRG_STAT_NOT_CHRGING: |
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; |
|
break; |
|
case BQ256XX_CHRG_STAT_PRECHRGING: |
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
|
break; |
|
case BQ256XX_CHRG_STAT_FAST_CHRGING: |
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; |
|
break; |
|
case BQ256XX_CHRG_STAT_CHRG_TERM: |
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
|
break; |
|
default: |
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; |
|
} |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_MANUFACTURER: |
|
val->strval = BQ256XX_MANUFACTURER; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_MODEL_NAME: |
|
val->strval = bq->model_name; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_ONLINE: |
|
val->intval = state.online; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
|
ret = bq->chip_info->bq256xx_get_vindpm(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
|
ret = bq->chip_info->bq256xx_get_iindpm(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
|
ret = bq->chip_info->bq256xx_get_vbatreg(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
|
ret = bq->chip_info->bq256xx_get_ichg(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: |
|
ret = bq->chip_info->bq256xx_get_iprechg(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: |
|
ret = bq->chip_info->bq256xx_get_iterm(bq); |
|
if (ret < 0) |
|
return ret; |
|
val->intval = ret; |
|
break; |
|
|
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static bool bq256xx_state_changed(struct bq256xx_device *bq, |
|
struct bq256xx_state *new_state) |
|
{ |
|
struct bq256xx_state old_state; |
|
|
|
mutex_lock(&bq->lock); |
|
old_state = bq->state; |
|
mutex_unlock(&bq->lock); |
|
|
|
return memcmp(&old_state, new_state, sizeof(struct bq256xx_state)) != 0; |
|
} |
|
|
|
static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private) |
|
{ |
|
struct bq256xx_device *bq = private; |
|
struct bq256xx_state state; |
|
int ret; |
|
|
|
ret = bq256xx_get_state(bq, &state); |
|
if (ret < 0) |
|
goto irq_out; |
|
|
|
if (!bq256xx_state_changed(bq, &state)) |
|
goto irq_out; |
|
|
|
mutex_lock(&bq->lock); |
|
bq->state = state; |
|
mutex_unlock(&bq->lock); |
|
|
|
power_supply_changed(bq->charger); |
|
|
|
irq_out: |
|
return IRQ_HANDLED; |
|
} |
|
|
|
static enum power_supply_property bq256xx_power_supply_props[] = { |
|
POWER_SUPPLY_PROP_MANUFACTURER, |
|
POWER_SUPPLY_PROP_MODEL_NAME, |
|
POWER_SUPPLY_PROP_STATUS, |
|
POWER_SUPPLY_PROP_ONLINE, |
|
POWER_SUPPLY_PROP_HEALTH, |
|
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, |
|
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, |
|
POWER_SUPPLY_PROP_CHARGE_TYPE, |
|
POWER_SUPPLY_PROP_USB_TYPE, |
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, |
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, |
|
POWER_SUPPLY_PROP_PRECHARGE_CURRENT, |
|
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, |
|
}; |
|
|
|
static enum power_supply_property bq256xx_battery_props[] = { |
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, |
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, |
|
}; |
|
|
|
static int bq256xx_property_is_writeable(struct power_supply *psy, |
|
enum power_supply_property prop) |
|
{ |
|
switch (prop) { |
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: |
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: |
|
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: |
|
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: |
|
case POWER_SUPPLY_PROP_STATUS: |
|
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: |
|
return true; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
static const struct power_supply_desc bq256xx_power_supply_desc = { |
|
.name = "bq256xx-charger", |
|
.type = POWER_SUPPLY_TYPE_USB, |
|
.usb_types = bq256xx_usb_type, |
|
.num_usb_types = ARRAY_SIZE(bq256xx_usb_type), |
|
.properties = bq256xx_power_supply_props, |
|
.num_properties = ARRAY_SIZE(bq256xx_power_supply_props), |
|
.get_property = bq256xx_get_charger_property, |
|
.set_property = bq256xx_set_charger_property, |
|
.property_is_writeable = bq256xx_property_is_writeable, |
|
}; |
|
|
|
static struct power_supply_desc bq256xx_battery_desc = { |
|
.name = "bq256xx-battery", |
|
.type = POWER_SUPPLY_TYPE_BATTERY, |
|
.get_property = bq256xx_get_battery_property, |
|
.properties = bq256xx_battery_props, |
|
.num_properties = ARRAY_SIZE(bq256xx_battery_props), |
|
.property_is_writeable = bq256xx_property_is_writeable, |
|
}; |
|
|
|
|
|
static bool bq256xx_is_volatile_reg(struct device *dev, unsigned int reg) |
|
{ |
|
switch (reg) { |
|
case BQ256XX_INPUT_CURRENT_LIMIT: |
|
case BQ256XX_CHARGER_STATUS_0...BQ256XX_CHARGER_STATUS_2: |
|
return true; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
static const struct regmap_config bq25600_regmap_config = { |
|
.reg_bits = 8, |
|
.val_bits = 8, |
|
|
|
.max_register = BQ256XX_PART_INFORMATION, |
|
.reg_defaults = bq2560x_reg_defs, |
|
.num_reg_defaults = ARRAY_SIZE(bq2560x_reg_defs), |
|
.cache_type = REGCACHE_FLAT, |
|
.volatile_reg = bq256xx_is_volatile_reg, |
|
}; |
|
|
|
static const struct regmap_config bq25611d_regmap_config = { |
|
.reg_bits = 8, |
|
.val_bits = 8, |
|
|
|
.max_register = BQ256XX_CHARGER_CONTROL_4, |
|
.reg_defaults = bq25611d_reg_defs, |
|
.num_reg_defaults = ARRAY_SIZE(bq25611d_reg_defs), |
|
.cache_type = REGCACHE_FLAT, |
|
.volatile_reg = bq256xx_is_volatile_reg, |
|
}; |
|
|
|
static const struct regmap_config bq25618_619_regmap_config = { |
|
.reg_bits = 8, |
|
.val_bits = 8, |
|
|
|
.max_register = BQ256XX_CHARGER_CONTROL_4, |
|
.reg_defaults = bq25618_619_reg_defs, |
|
.num_reg_defaults = ARRAY_SIZE(bq25618_619_reg_defs), |
|
.cache_type = REGCACHE_FLAT, |
|
.volatile_reg = bq256xx_is_volatile_reg, |
|
}; |
|
|
|
static const struct bq256xx_chip_info bq256xx_chip_info_tbl[] = { |
|
[BQ25600] = { |
|
.model_id = BQ25600, |
|
.bq256xx_regmap_config = &bq25600_regmap_config, |
|
.bq256xx_get_ichg = bq256xx_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq2560x_get_chrg_volt, |
|
.bq256xx_get_iterm = bq256xx_get_term_curr, |
|
.bq256xx_get_iprechg = bq256xx_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq256xx_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq2560x_set_chrg_volt, |
|
.bq256xx_set_iterm = bq256xx_set_term_curr, |
|
.bq256xx_set_iprechg = bq256xx_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = false, |
|
}, |
|
|
|
[BQ25600D] = { |
|
.model_id = BQ25600D, |
|
.bq256xx_regmap_config = &bq25600_regmap_config, |
|
.bq256xx_get_ichg = bq256xx_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq2560x_get_chrg_volt, |
|
.bq256xx_get_iterm = bq256xx_get_term_curr, |
|
.bq256xx_get_iprechg = bq256xx_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq256xx_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq2560x_set_chrg_volt, |
|
.bq256xx_set_iterm = bq256xx_set_term_curr, |
|
.bq256xx_set_iprechg = bq256xx_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = true, |
|
}, |
|
|
|
[BQ25601] = { |
|
.model_id = BQ25601, |
|
.bq256xx_regmap_config = &bq25600_regmap_config, |
|
.bq256xx_get_ichg = bq256xx_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq2560x_get_chrg_volt, |
|
.bq256xx_get_iterm = bq256xx_get_term_curr, |
|
.bq256xx_get_iprechg = bq256xx_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq256xx_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq2560x_set_chrg_volt, |
|
.bq256xx_set_iterm = bq256xx_set_term_curr, |
|
.bq256xx_set_iprechg = bq256xx_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = false, |
|
}, |
|
|
|
[BQ25601D] = { |
|
.model_id = BQ25601D, |
|
.bq256xx_regmap_config = &bq25600_regmap_config, |
|
.bq256xx_get_ichg = bq256xx_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq25601d_get_chrg_volt, |
|
.bq256xx_get_iterm = bq256xx_get_term_curr, |
|
.bq256xx_get_iprechg = bq256xx_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq256xx_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq25601d_set_chrg_volt, |
|
.bq256xx_set_iterm = bq256xx_set_term_curr, |
|
.bq256xx_set_iprechg = bq256xx_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = true, |
|
}, |
|
|
|
[BQ25611D] = { |
|
.model_id = BQ25611D, |
|
.bq256xx_regmap_config = &bq25611d_regmap_config, |
|
.bq256xx_get_ichg = bq256xx_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq25611d_get_chrg_volt, |
|
.bq256xx_get_iterm = bq256xx_get_term_curr, |
|
.bq256xx_get_iprechg = bq256xx_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq256xx_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq25611d_set_chrg_volt, |
|
.bq256xx_set_iterm = bq256xx_set_term_curr, |
|
.bq256xx_set_iprechg = bq256xx_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ25611D_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ25611D_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ25611D_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = true, |
|
}, |
|
|
|
[BQ25618] = { |
|
.model_id = BQ25618, |
|
.bq256xx_regmap_config = &bq25618_619_regmap_config, |
|
.bq256xx_get_ichg = bq25618_619_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq25618_619_get_chrg_volt, |
|
.bq256xx_get_iterm = bq25618_619_get_term_curr, |
|
.bq256xx_get_iprechg = bq25618_619_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq25618_619_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq25618_619_set_chrg_volt, |
|
.bq256xx_set_iterm = bq25618_619_set_term_curr, |
|
.bq256xx_set_iprechg = bq25618_619_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ25618_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ25618_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ25618_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ25618_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ25618_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ25618_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = false, |
|
}, |
|
|
|
[BQ25619] = { |
|
.model_id = BQ25619, |
|
.bq256xx_regmap_config = &bq25618_619_regmap_config, |
|
.bq256xx_get_ichg = bq25618_619_get_ichg_curr, |
|
.bq256xx_get_iindpm = bq256xx_get_input_curr_lim, |
|
.bq256xx_get_vbatreg = bq25618_619_get_chrg_volt, |
|
.bq256xx_get_iterm = bq25618_619_get_term_curr, |
|
.bq256xx_get_iprechg = bq25618_619_get_prechrg_curr, |
|
.bq256xx_get_vindpm = bq256xx_get_input_volt_lim, |
|
|
|
.bq256xx_set_ichg = bq25618_619_set_ichg_curr, |
|
.bq256xx_set_iindpm = bq256xx_set_input_curr_lim, |
|
.bq256xx_set_vbatreg = bq25618_619_set_chrg_volt, |
|
.bq256xx_set_iterm = bq25618_619_set_term_curr, |
|
.bq256xx_set_iprechg = bq25618_619_set_prechrg_curr, |
|
.bq256xx_set_vindpm = bq256xx_set_input_volt_lim, |
|
|
|
.bq256xx_def_ichg = BQ25618_ICHG_DEF_uA, |
|
.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA, |
|
.bq256xx_def_vbatreg = BQ25618_VBATREG_DEF_uV, |
|
.bq256xx_def_iterm = BQ25618_ITERM_DEF_uA, |
|
.bq256xx_def_iprechg = BQ25618_IPRECHG_DEF_uA, |
|
.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV, |
|
|
|
.bq256xx_max_ichg = BQ25618_ICHG_MAX_uA, |
|
.bq256xx_max_vbatreg = BQ25618_VBATREG_MAX_uV, |
|
|
|
.has_usb_detect = false, |
|
}, |
|
}; |
|
|
|
static int bq256xx_power_supply_init(struct bq256xx_device *bq, |
|
struct power_supply_config *psy_cfg, struct device *dev) |
|
{ |
|
bq->charger = devm_power_supply_register(bq->dev, |
|
&bq256xx_power_supply_desc, |
|
psy_cfg); |
|
if (IS_ERR(bq->charger)) { |
|
dev_err(dev, "power supply register charger failed\n"); |
|
return PTR_ERR(bq->charger); |
|
} |
|
|
|
bq->battery = devm_power_supply_register(bq->dev, |
|
&bq256xx_battery_desc, |
|
psy_cfg); |
|
if (IS_ERR(bq->battery)) { |
|
dev_err(dev, "power supply register battery failed\n"); |
|
return PTR_ERR(bq->battery); |
|
} |
|
return 0; |
|
} |
|
|
|
static int bq256xx_hw_init(struct bq256xx_device *bq) |
|
{ |
|
struct power_supply_battery_info bat_info = { }; |
|
int wd_reg_val = BQ256XX_WATCHDOG_DIS; |
|
int ret = 0; |
|
int i; |
|
|
|
for (i = 0; i < BQ256XX_NUM_WD_VAL; i++) { |
|
if (bq->watchdog_timer == bq256xx_watchdog_time[i]) { |
|
wd_reg_val = i; |
|
break; |
|
} |
|
if (bq->watchdog_timer > bq256xx_watchdog_time[i] && |
|
bq->watchdog_timer < bq256xx_watchdog_time[i + 1]) |
|
wd_reg_val = i; |
|
} |
|
ret = regmap_update_bits(bq->regmap, BQ256XX_CHARGER_CONTROL_1, |
|
BQ256XX_WATCHDOG_MASK, wd_reg_val << |
|
BQ256XX_WDT_BIT_SHIFT); |
|
|
|
ret = power_supply_get_battery_info(bq->charger, &bat_info); |
|
if (ret) { |
|
dev_warn(bq->dev, "battery info missing, default values will be applied\n"); |
|
|
|
bat_info.constant_charge_current_max_ua = |
|
bq->chip_info->bq256xx_def_ichg; |
|
|
|
bat_info.constant_charge_voltage_max_uv = |
|
bq->chip_info->bq256xx_def_vbatreg; |
|
|
|
bat_info.precharge_current_ua = |
|
bq->chip_info->bq256xx_def_iprechg; |
|
|
|
bat_info.charge_term_current_ua = |
|
bq->chip_info->bq256xx_def_iterm; |
|
|
|
bq->init_data.ichg_max = |
|
bq->chip_info->bq256xx_max_ichg; |
|
|
|
bq->init_data.vbatreg_max = |
|
bq->chip_info->bq256xx_max_vbatreg; |
|
} else { |
|
bq->init_data.ichg_max = |
|
bat_info.constant_charge_current_max_ua; |
|
|
|
bq->init_data.vbatreg_max = |
|
bat_info.constant_charge_voltage_max_uv; |
|
} |
|
|
|
ret = bq->chip_info->bq256xx_set_vindpm(bq, bq->init_data.vindpm); |
|
if (ret) |
|
return ret; |
|
|
|
ret = bq->chip_info->bq256xx_set_iindpm(bq, bq->init_data.iindpm); |
|
if (ret) |
|
return ret; |
|
|
|
ret = bq->chip_info->bq256xx_set_ichg(bq, |
|
bat_info.constant_charge_current_max_ua); |
|
if (ret) |
|
return ret; |
|
|
|
ret = bq->chip_info->bq256xx_set_iprechg(bq, |
|
bat_info.precharge_current_ua); |
|
if (ret) |
|
return ret; |
|
|
|
ret = bq->chip_info->bq256xx_set_vbatreg(bq, |
|
bat_info.constant_charge_voltage_max_uv); |
|
if (ret) |
|
return ret; |
|
|
|
ret = bq->chip_info->bq256xx_set_iterm(bq, |
|
bat_info.charge_term_current_ua); |
|
if (ret) |
|
return ret; |
|
|
|
power_supply_put_battery_info(bq->charger, &bat_info); |
|
|
|
return 0; |
|
} |
|
|
|
static int bq256xx_parse_dt(struct bq256xx_device *bq, |
|
struct power_supply_config *psy_cfg, struct device *dev) |
|
{ |
|
int ret = 0; |
|
|
|
psy_cfg->drv_data = bq; |
|
psy_cfg->of_node = dev->of_node; |
|
|
|
ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms", |
|
&bq->watchdog_timer); |
|
if (ret) |
|
bq->watchdog_timer = BQ256XX_WATCHDOG_DIS; |
|
|
|
if (bq->watchdog_timer > BQ256XX_WATCHDOG_MAX || |
|
bq->watchdog_timer < BQ256XX_WATCHDOG_DIS) |
|
return -EINVAL; |
|
|
|
ret = device_property_read_u32(bq->dev, |
|
"input-voltage-limit-microvolt", |
|
&bq->init_data.vindpm); |
|
if (ret) |
|
bq->init_data.vindpm = bq->chip_info->bq256xx_def_vindpm; |
|
|
|
ret = device_property_read_u32(bq->dev, |
|
"input-current-limit-microamp", |
|
&bq->init_data.iindpm); |
|
if (ret) |
|
bq->init_data.iindpm = bq->chip_info->bq256xx_def_iindpm; |
|
|
|
return 0; |
|
} |
|
|
|
static int bq256xx_probe(struct i2c_client *client, |
|
const struct i2c_device_id *id) |
|
{ |
|
struct device *dev = &client->dev; |
|
struct bq256xx_device *bq; |
|
struct power_supply_config psy_cfg = { }; |
|
|
|
int ret; |
|
|
|
bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); |
|
if (!bq) |
|
return -ENOMEM; |
|
|
|
bq->client = client; |
|
bq->dev = dev; |
|
bq->chip_info = &bq256xx_chip_info_tbl[id->driver_data]; |
|
|
|
mutex_init(&bq->lock); |
|
|
|
strncpy(bq->model_name, id->name, I2C_NAME_SIZE); |
|
|
|
bq->regmap = devm_regmap_init_i2c(client, |
|
bq->chip_info->bq256xx_regmap_config); |
|
|
|
if (IS_ERR(bq->regmap)) { |
|
dev_err(dev, "Failed to allocate register map\n"); |
|
return PTR_ERR(bq->regmap); |
|
} |
|
|
|
i2c_set_clientdata(client, bq); |
|
|
|
ret = bq256xx_parse_dt(bq, &psy_cfg, dev); |
|
if (ret) { |
|
dev_err(dev, "Failed to read device tree properties%d\n", ret); |
|
return ret; |
|
} |
|
|
|
ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq); |
|
if (ret) |
|
return ret; |
|
|
|
/* OTG reporting */ |
|
bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); |
|
if (!IS_ERR_OR_NULL(bq->usb2_phy)) { |
|
INIT_WORK(&bq->usb_work, bq256xx_usb_work); |
|
bq->usb_nb.notifier_call = bq256xx_usb_notifier; |
|
usb_register_notifier(bq->usb2_phy, &bq->usb_nb); |
|
} |
|
|
|
bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); |
|
if (!IS_ERR_OR_NULL(bq->usb3_phy)) { |
|
INIT_WORK(&bq->usb_work, bq256xx_usb_work); |
|
bq->usb_nb.notifier_call = bq256xx_usb_notifier; |
|
usb_register_notifier(bq->usb3_phy, &bq->usb_nb); |
|
} |
|
|
|
if (client->irq) { |
|
ret = devm_request_threaded_irq(dev, client->irq, NULL, |
|
bq256xx_irq_handler_thread, |
|
IRQF_TRIGGER_FALLING | |
|
IRQF_ONESHOT, |
|
dev_name(&client->dev), bq); |
|
if (ret < 0) { |
|
dev_err(dev, "get irq fail: %d\n", ret); |
|
return ret; |
|
} |
|
} |
|
|
|
ret = bq256xx_power_supply_init(bq, &psy_cfg, dev); |
|
if (ret) { |
|
dev_err(dev, "Failed to register power supply\n"); |
|
return ret; |
|
} |
|
|
|
ret = bq256xx_hw_init(bq); |
|
if (ret) { |
|
dev_err(dev, "Cannot initialize the chip.\n"); |
|
return ret; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static const struct i2c_device_id bq256xx_i2c_ids[] = { |
|
{ "bq25600", BQ25600 }, |
|
{ "bq25600d", BQ25600D }, |
|
{ "bq25601", BQ25601 }, |
|
{ "bq25601d", BQ25601D }, |
|
{ "bq25611d", BQ25611D }, |
|
{ "bq25618", BQ25618 }, |
|
{ "bq25619", BQ25619 }, |
|
{}, |
|
}; |
|
MODULE_DEVICE_TABLE(i2c, bq256xx_i2c_ids); |
|
|
|
static const struct of_device_id bq256xx_of_match[] = { |
|
{ .compatible = "ti,bq25600", .data = (void *)BQ25600 }, |
|
{ .compatible = "ti,bq25600d", .data = (void *)BQ25600D }, |
|
{ .compatible = "ti,bq25601", .data = (void *)BQ25601 }, |
|
{ .compatible = "ti,bq25601d", .data = (void *)BQ25601D }, |
|
{ .compatible = "ti,bq25611d", .data = (void *)BQ25611D }, |
|
{ .compatible = "ti,bq25618", .data = (void *)BQ25618 }, |
|
{ .compatible = "ti,bq25619", .data = (void *)BQ25619 }, |
|
{ }, |
|
}; |
|
MODULE_DEVICE_TABLE(of, bq256xx_of_match); |
|
|
|
static const struct acpi_device_id bq256xx_acpi_match[] = { |
|
{ "bq25600", BQ25600 }, |
|
{ "bq25600d", BQ25600D }, |
|
{ "bq25601", BQ25601 }, |
|
{ "bq25601d", BQ25601D }, |
|
{ "bq25611d", BQ25611D }, |
|
{ "bq25618", BQ25618 }, |
|
{ "bq25619", BQ25619 }, |
|
{}, |
|
}; |
|
MODULE_DEVICE_TABLE(acpi, bq256xx_acpi_match); |
|
|
|
static struct i2c_driver bq256xx_driver = { |
|
.driver = { |
|
.name = "bq256xx-charger", |
|
.of_match_table = bq256xx_of_match, |
|
.acpi_match_table = bq256xx_acpi_match, |
|
}, |
|
.probe = bq256xx_probe, |
|
.id_table = bq256xx_i2c_ids, |
|
}; |
|
module_i2c_driver(bq256xx_driver); |
|
|
|
MODULE_AUTHOR("Ricardo Rivera-Matos <[email protected]>"); |
|
MODULE_DESCRIPTION("bq256xx charger driver"); |
|
MODULE_LICENSE("GPL v2");
|
|
|