Commit 28e54414 authored by Sebastian Reichel's avatar Sebastian Reichel

Merge tag 'psy-extcon-i2c-mfd-for-v5.18-signed' into psy-next

Immutable branch between power-supply, mfd, i2c and extcon for for 5.18

This immutable branch fixes the charger setup on Xiaomi Mi Pad 2 and
Lenovo Yogabook, which requires updates to multiple drivers throughout
the tree.
Signed-off-by: default avatarSebastian Reichel <sre@kernel.org>
parents 210bc22c 21356ac1
......@@ -61,6 +61,8 @@ config EXTCON_INTEL_INT3496
config EXTCON_INTEL_CHT_WC
tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver"
depends on INTEL_SOC_PMIC_CHTWC
depends on USB_SUPPORT
select USB_ROLE_SWITCH
help
Say Y here to enable extcon support for charger detection / control
on the Intel Cherrytrail Whiskey Cove PMIC.
......
......@@ -14,8 +14,12 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/role.h>
#include "extcon-intel.h"
......@@ -101,8 +105,13 @@ struct cht_wc_extcon_data {
struct device *dev;
struct regmap *regmap;
struct extcon_dev *edev;
struct usb_role_switch *role_sw;
struct regulator *vbus_boost;
struct power_supply *psy;
enum power_supply_usb_type usb_type;
unsigned int previous_cable;
bool usb_host;
bool vbus_boost_enabled;
};
static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
......@@ -112,13 +121,21 @@ static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
return INTEL_USB_ID_GND;
case CHT_WC_PWRSRC_RID_FLOAT:
return INTEL_USB_ID_FLOAT;
/*
* According to the spec. we should read the USB-ID pin ADC value here
* to determine the resistance of the used pull-down resister and then
* return RID_A / RID_B / RID_C based on this. But all "Accessory
* Charger Adapter"s (ACAs) which users can actually buy always use
* a combination of a charging port with one or more USB-A ports, so
* they should always use a resistor indicating RID_A. But the spec
* is hard to read / badly-worded so some of them actually indicate
* they are a RID_B ACA evnen though they clearly are a RID_A ACA.
* To workaround this simply always return INTEL_USB_RID_A, which
* matches all the ACAs which users can actually buy.
*/
case CHT_WC_PWRSRC_RID_ACA:
return INTEL_USB_RID_A;
default:
/*
* Once we have IIO support for the GPADC we should read
* the USBID GPADC channel here and determine ACA role
* based on that.
*/
return INTEL_USB_ID_FLOAT;
}
}
......@@ -147,14 +164,15 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
} while (time_before(jiffies, timeout));
if (status != CHT_WC_USBSRC_STS_SUCCESS) {
if (ignore_errors)
return EXTCON_CHG_USB_SDP; /* Save fallback */
if (!ignore_errors) {
if (status == CHT_WC_USBSRC_STS_FAIL)
dev_warn(ext->dev, "Could not detect charger type\n");
else
dev_warn(ext->dev, "Timeout detecting charger type\n");
}
if (status == CHT_WC_USBSRC_STS_FAIL)
dev_warn(ext->dev, "Could not detect charger type\n");
else
dev_warn(ext->dev, "Timeout detecting charger type\n");
return EXTCON_CHG_USB_SDP; /* Save fallback */
/* Safe fallback */
usbsrc = CHT_WC_USBSRC_TYPE_SDP << CHT_WC_USBSRC_TYPE_SHIFT;
}
usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT;
......@@ -163,18 +181,23 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
dev_warn(ext->dev,
"Unhandled charger type %d, defaulting to SDP\n",
ret);
ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_SDP:
case CHT_WC_USBSRC_TYPE_FLOATING:
case CHT_WC_USBSRC_TYPE_OTHER:
ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_CDP:
ext->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
return EXTCON_CHG_USB_CDP;
case CHT_WC_USBSRC_TYPE_DCP:
case CHT_WC_USBSRC_TYPE_DCP_EXTPHY:
case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */
ext->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
return EXTCON_CHG_USB_DCP;
case CHT_WC_USBSRC_TYPE_ACA:
ext->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
return EXTCON_CHG_USB_ACA;
}
}
......@@ -216,6 +239,18 @@ static void cht_wc_extcon_set_otgmode(struct cht_wc_extcon_data *ext,
CHT_WC_CHGRCTRL1_OTGMODE, val);
if (ret)
dev_err(ext->dev, "Error updating CHGRCTRL1 reg: %d\n", ret);
if (ext->vbus_boost && ext->vbus_boost_enabled != enable) {
if (enable)
ret = regulator_enable(ext->vbus_boost);
else
ret = regulator_disable(ext->vbus_boost);
if (ret)
dev_err(ext->dev, "Error updating Vbus boost regulator: %d\n", ret);
else
ext->vbus_boost_enabled = enable;
}
}
static void cht_wc_extcon_enable_charging(struct cht_wc_extcon_data *ext,
......@@ -245,6 +280,9 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
unsigned int cable = EXTCON_NONE;
/* Ignore errors in host mode, as the 5v boost converter is on then */
bool ignore_get_charger_errors = ext->usb_host;
enum usb_role role;
ext->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
if (ret) {
......@@ -288,6 +326,21 @@ static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
ext->usb_host = ((id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A));
extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host);
if (ext->usb_host)
role = USB_ROLE_HOST;
else if (pwrsrc_sts & CHT_WC_PWRSRC_VBUS)
role = USB_ROLE_DEVICE;
else
role = USB_ROLE_NONE;
/* Note: this is a no-op when ext->role_sw is NULL */
ret = usb_role_switch_set_role(ext->role_sw, role);
if (ret)
dev_err(ext->dev, "Error setting USB-role: %d\n", ret);
if (ext->psy)
power_supply_changed(ext->psy);
}
static irqreturn_t cht_wc_extcon_isr(int irq, void *data)
......@@ -333,6 +386,114 @@ static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
return ret;
}
static int cht_wc_extcon_find_role_sw(struct cht_wc_extcon_data *ext)
{
const struct software_node *swnode;
struct fwnode_handle *fwnode;
swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
if (!swnode)
return -EPROBE_DEFER;
fwnode = software_node_fwnode(swnode);
ext->role_sw = usb_role_switch_find_by_fwnode(fwnode);
fwnode_handle_put(fwnode);
return ext->role_sw ? 0 : -EPROBE_DEFER;
}
static void cht_wc_extcon_put_role_sw(void *data)
{
struct cht_wc_extcon_data *ext = data;
usb_role_switch_put(ext->role_sw);
}
/* Some boards require controlling the role-sw and Vbus based on the id-pin */
static int cht_wc_extcon_get_role_sw_and_regulator(struct cht_wc_extcon_data *ext)
{
int ret;
ret = cht_wc_extcon_find_role_sw(ext);
if (ret)
return ret;
ret = devm_add_action_or_reset(ext->dev, cht_wc_extcon_put_role_sw, ext);
if (ret)
return ret;
/*
* On x86/ACPI platforms the regulator <-> consumer link is provided
* by platform_data passed to the regulator driver. This means that
* this info is not available before the regulator driver has bound.
* Use devm_regulator_get_optional() to avoid getting a dummy
* regulator and wait for the regulator to show up if necessary.
*/
ext->vbus_boost = devm_regulator_get_optional(ext->dev, "vbus");
if (IS_ERR(ext->vbus_boost)) {
ret = PTR_ERR(ext->vbus_boost);
if (ret == -ENODEV)
ret = -EPROBE_DEFER;
return dev_err_probe(ext->dev, ret, "getting Vbus regulator");
}
return 0;
}
static int cht_wc_extcon_psy_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct cht_wc_extcon_data *ext = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_USB_TYPE:
val->intval = ext->usb_type;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = ext->usb_type ? 1 : 0;
break;
default:
return -EINVAL;
}
return 0;
}
static const enum power_supply_usb_type cht_wc_extcon_psy_usb_types[] = {
POWER_SUPPLY_USB_TYPE_SDP,
POWER_SUPPLY_USB_TYPE_CDP,
POWER_SUPPLY_USB_TYPE_DCP,
POWER_SUPPLY_USB_TYPE_ACA,
POWER_SUPPLY_USB_TYPE_UNKNOWN,
};
static const enum power_supply_property cht_wc_extcon_psy_props[] = {
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
};
static const struct power_supply_desc cht_wc_extcon_psy_desc = {
.name = "cht_wcove_pwrsrc",
.type = POWER_SUPPLY_TYPE_USB,
.usb_types = cht_wc_extcon_psy_usb_types,
.num_usb_types = ARRAY_SIZE(cht_wc_extcon_psy_usb_types),
.properties = cht_wc_extcon_psy_props,
.num_properties = ARRAY_SIZE(cht_wc_extcon_psy_props),
.get_property = cht_wc_extcon_psy_get_prop,
};
static int cht_wc_extcon_register_psy(struct cht_wc_extcon_data *ext)
{
struct power_supply_config psy_cfg = { .drv_data = ext };
ext->psy = devm_power_supply_register(ext->dev,
&cht_wc_extcon_psy_desc,
&psy_cfg);
return PTR_ERR_OR_ZERO(ext->psy);
}
static int cht_wc_extcon_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
......@@ -358,20 +519,47 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
if (IS_ERR(ext->edev))
return PTR_ERR(ext->edev);
/*
* When a host-cable is detected the BIOS enables an external 5v boost
* converter to power connected devices there are 2 problems with this:
* 1) This gets seen by the external battery charger as a valid Vbus
* supply and it then tries to feed Vsys from this creating a
* feedback loop which causes aprox. 300 mA extra battery drain
* (and unless we drive the external-charger-disable pin high it
* also tries to charge the battery causing even more feedback).
* 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
* Since the external battery charger has its own 5v boost converter
* which does not have these issues, we simply turn the separate
* external 5v boost converter off and leave it off entirely.
*/
cht_wc_extcon_set_5v_boost(ext, false);
switch (pmic->cht_wc_model) {
case INTEL_CHT_WC_GPD_WIN_POCKET:
/*
* When a host-cable is detected the BIOS enables an external 5v boost
* converter to power connected devices there are 2 problems with this:
* 1) This gets seen by the external battery charger as a valid Vbus
* supply and it then tries to feed Vsys from this creating a
* feedback loop which causes aprox. 300 mA extra battery drain
* (and unless we drive the external-charger-disable pin high it
* also tries to charge the battery causing even more feedback).
* 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
* Since the external battery charger has its own 5v boost converter
* which does not have these issues, we simply turn the separate
* external 5v boost converter off and leave it off entirely.
*/
cht_wc_extcon_set_5v_boost(ext, false);
break;
case INTEL_CHT_WC_LENOVO_YOGABOOK1:
/* Do this first, as it may very well return -EPROBE_DEFER. */
ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
if (ret)
return ret;
/*
* The bq25890 used here relies on this driver's BC-1.2 charger
* detection, and the bq25890 driver expect this info to be
* available through a parent power_supply class device which
* models the detected charger (idem to how the Type-C TCPM code
* registers a power_supply classdev for the connected charger).
*/
ret = cht_wc_extcon_register_psy(ext);
if (ret)
return ret;
break;
case INTEL_CHT_WC_XIAOMI_MIPAD2:
ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
if (ret)
return ret;
break;
default:
break;
}
/* Enable sw control */
ret = cht_wc_extcon_sw_control(ext, true);
......
......@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power/bq24190_charger.h>
#include <linux/power/bq25890_charger.h>
#include <linux/slab.h>
#define CHT_WC_I2C_CTRL 0x5e24
......@@ -270,6 +271,7 @@ static const struct irq_chip cht_wc_i2c_irq_chip = {
.name = "cht_wc_ext_chrg_irq_chip",
};
/********** GPD Win / Pocket charger IC settings **********/
static const char * const bq24190_suppliers[] = {
"tcpm-source-psy-i2c-fusb302" };
......@@ -304,17 +306,92 @@ static struct bq24190_platform_data bq24190_pdata = {
.regulator_init_data = &bq24190_vbus_init_data,
};
static struct i2c_board_info gpd_win_board_info = {
.type = "bq24190",
.addr = 0x6b,
.dev_name = "bq24190",
.swnode = &bq24190_node,
.platform_data = &bq24190_pdata,
};
/********** Xiaomi Mi Pad 2 charger IC settings **********/
static struct regulator_consumer_supply bq2589x_vbus_consumer = {
.supply = "vbus",
.dev_name = "cht_wcove_pwrsrc",
};
static const struct regulator_init_data bq2589x_vbus_init_data = {
.constraints = {
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.consumer_supplies = &bq2589x_vbus_consumer,
.num_consumer_supplies = 1,
};
static struct bq25890_platform_data bq2589x_pdata = {
.regulator_init_data = &bq2589x_vbus_init_data,
};
static const struct property_entry xiaomi_mipad2_props[] = {
PROPERTY_ENTRY_BOOL("linux,skip-reset"),
PROPERTY_ENTRY_BOOL("linux,read-back-settings"),
{ }
};
static const struct software_node xiaomi_mipad2_node = {
.properties = xiaomi_mipad2_props,
};
static struct i2c_board_info xiaomi_mipad2_board_info = {
.type = "bq25890",
.addr = 0x6a,
.dev_name = "bq25890",
.swnode = &xiaomi_mipad2_node,
.platform_data = &bq2589x_pdata,
};
/********** Lenovo Yogabook YB1-X90F/-X91F/-X91L charger settings **********/
static const char * const lenovo_yb1_bq25892_suppliers[] = { "cht_wcove_pwrsrc" };
static const struct property_entry lenovo_yb1_bq25892_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from",
lenovo_yb1_bq25892_suppliers),
PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
PROPERTY_ENTRY_BOOL("linux,skip-reset"),
/*
* The firmware sets everything to the defaults, which leads to a
* somewhat low charge-current of 2048mA and worse to a battery-voltage
* of 4.2V instead of 4.35V (when booted without a charger connected).
* Use our own values instead of "linux,read-back-settings" to fix this.
*/
PROPERTY_ENTRY_U32("ti,charge-current", 4224000),
PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
PROPERTY_ENTRY_U32("ti,termination-current", 256000),
PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3500000),
PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
PROPERTY_ENTRY_U32("ti,boost-max-current", 1400000),
PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
{ }
};
static const struct software_node lenovo_yb1_bq25892_node = {
.properties = lenovo_yb1_bq25892_props,
};
static struct i2c_board_info lenovo_yogabook1_board_info = {
.type = "bq25892",
.addr = 0x6b,
.dev_name = "bq25892",
.swnode = &lenovo_yb1_bq25892_node,
.platform_data = &bq2589x_pdata,
};
static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct i2c_board_info *board_info = NULL;
struct cht_wc_i2c_adap *adap;
struct i2c_board_info board_info = {
.type = "bq24190",
.addr = 0x6b,
.dev_name = "bq24190",
.swnode = &bq24190_node,
.platform_data = &bq24190_pdata,
};
int ret, reg, irq;
irq = platform_get_irq(pdev, 0);
......@@ -379,17 +456,24 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
if (ret)
goto remove_irq_domain;
/*
* Normally the Whiskey Cove PMIC is paired with a TI bq24292i charger,
* connected to this i2c bus, and a max17047 fuel-gauge and a fusb302
* USB Type-C controller connected to another i2c bus. In this setup
* the max17047 and fusb302 devices are enumerated through an INT33FE
* ACPI device. If this device is present register an i2c-client for
* the TI bq24292i charger.
*/
if (acpi_dev_present("INT33FE", NULL, -1)) {
board_info.irq = adap->client_irq;
adap->client = i2c_new_client_device(&adap->adapter, &board_info);
switch (pmic->cht_wc_model) {
case INTEL_CHT_WC_GPD_WIN_POCKET:
board_info = &gpd_win_board_info;
break;
case INTEL_CHT_WC_XIAOMI_MIPAD2:
board_info = &xiaomi_mipad2_board_info;
break;
case INTEL_CHT_WC_LENOVO_YOGABOOK1:
board_info = &lenovo_yogabook1_board_info;
break;
default:
dev_warn(&pdev->dev, "Unknown model, not instantiating charger device\n");
break;
}
if (board_info) {
board_info->irq = adap->client_irq;
adap->client = i2c_new_client_device(&adap->adapter, board_info);
if (IS_ERR(adap->client)) {
ret = PTR_ERR(adap->client);
goto del_adapter;
......
......@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
......@@ -134,9 +135,44 @@ static const struct regmap_irq_chip cht_wc_regmap_irq_chip = {
.num_regs = 1,
};
static const struct dmi_system_id cht_wc_model_dmi_ids[] = {
{
/* GPD win / GPD pocket mini laptops */
.driver_data = (void *)(long)INTEL_CHT_WC_GPD_WIN_POCKET,
/*
* This DMI match may not seem unique, but it is. In the 67000+
* DMI decode dumps from linux-hardware.org only 116 have
* board_vendor set to "AMI Corporation" and of those 116 only
* the GPD win's and pocket's board_name is "Default string".
*/
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
},
}, {
/* Xiaomi Mi Pad 2 */
.driver_data = (void *)(long)INTEL_CHT_WC_XIAOMI_MIPAD2,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"),
DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
},
}, {
/* Lenovo Yoga Book X90F / X91F / X91L */
.driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1,
.matches = {
/* Non exact match to match all versions */
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"),
},
},
{ }
};
static int cht_wc_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
const struct dmi_system_id *id;
struct intel_soc_pmic *pmic;
acpi_status status;
unsigned long long hrv;
......@@ -160,6 +196,10 @@ static int cht_wc_probe(struct i2c_client *client)
if (!pmic)
return -ENOMEM;
id = dmi_first_match(cht_wc_model_dmi_ids);
if (id)
pmic->cht_wc_model = (long)id->driver_data;
pmic->irq = client->irq;
pmic->dev = dev;
i2c_set_clientdata(client, pmic);
......
......@@ -1206,8 +1206,18 @@ static void bq24190_input_current_limit_work(struct work_struct *work)
struct bq24190_dev_info *bdi =
container_of(work, struct bq24190_dev_info,
input_current_limit_work.work);
union power_supply_propval val;
int ret;
ret = power_supply_get_property_from_supplier(bdi->charger,
POWER_SUPPLY_PROP_CURRENT_MAX,
&val);
if (ret)
return;
power_supply_set_input_current_limit_from_supplier(bdi->charger);
bq24190_charger_set_property(bdi->charger,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
&val);
}
/* Sync the input-current-limit with our parent supply (if we have one) */
......
......@@ -8,7 +8,9 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/power_supply.h>
#include <linux/power/bq25890_charger.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/types.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
......@@ -25,6 +27,10 @@
#define BQ25895_ID 7
#define BQ25896_ID 0
#define PUMP_EXPRESS_START_DELAY (5 * HZ)
#define PUMP_EXPRESS_MAX_TRIES 6
#define PUMP_EXPRESS_VBUS_MARGIN_uV 1000000
enum bq25890_chip_version {
BQ25890,
BQ25892,
......@@ -40,7 +46,7 @@ static const char *const bq25890_chip_name[] = {
};
enum bq25890_fields {
F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */
F_EN_HIZ, F_EN_ILIM, F_IINLIM, /* Reg00 */
F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */
F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */
......@@ -94,6 +100,7 @@ struct bq25890_state {
u8 vsys_status;
u8 boost_fault;
u8 bat_fault;
u8 ntc_fault;
};
struct bq25890_device {
......@@ -104,11 +111,15 @@ struct bq25890_device {
struct usb_phy *usb_phy;
struct notifier_block usb_nb;
struct work_struct usb_work;
struct delayed_work pump_express_work;
unsigned long usb_event;
struct regmap *rmap;
struct regmap_field *rmap_fields[F_MAX_FIELDS];
bool skip_reset;
bool read_back_init_data;
u32 pump_express_vbus_max;
enum bq25890_chip_version chip_version;
struct bq25890_init_data init_data;
struct bq25890_state state;
......@@ -153,7 +164,7 @@ static const struct reg_field bq25890_reg_fields[] = {
/* REG00 */
[F_EN_HIZ] = REG_FIELD(0x00, 7, 7),
[F_EN_ILIM] = REG_FIELD(0x00, 6, 6),
[F_IILIM] = REG_FIELD(0x00, 0, 5),
[F_IINLIM] = REG_FIELD(0x00, 0, 5),
/* REG01 */
[F_BHOT] = REG_FIELD(0x01, 6, 7),
[F_BCOLD] = REG_FIELD(0x01, 5, 5),
......@@ -256,10 +267,11 @@ enum bq25890_table_ids {
/* range tables */
TBL_ICHG,
TBL_ITERM,
TBL_IILIM,
TBL_IINLIM,
TBL_VREG,
TBL_BOOSTV,
TBL_SYSVMIN,
TBL_VBUSV,
TBL_VBATCOMP,
TBL_RBATCOMP,
......@@ -320,14 +332,15 @@ static const union {
} bq25890_tables[] = {
/* range tables */
/* TODO: BQ25896 has max ICHG 3008 mA */
[TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
[TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
[TBL_IILIM] = { .rt = {100000, 3250000, 50000} }, /* uA */
[TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
[TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */
[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */
[TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */
[TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */
[TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
[TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
[TBL_IINLIM] = { .rt = {100000, 3250000, 50000} }, /* uA */
[TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
[TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */
[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */
[TBL_VBUSV] = { .rt = {2600000, 15300000, 100000} }, /* uV */
[TBL_VBATCOMP] = { .rt = {0, 224000, 32000} }, /* uV */
[TBL_RBATCOMP] = { .rt = {0, 140000, 20000} }, /* uOhm */
/* lookup tables */
[TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
......@@ -407,6 +420,14 @@ enum bq25890_chrg_fault {
CHRG_FAULT_TIMER_EXPIRED,
};
enum bq25890_ntc_fault {
NTC_FAULT_NORMAL = 0,
NTC_FAULT_WARM = 2,
NTC_FAULT_COOL = 3,
NTC_FAULT_COLD = 5,
NTC_FAULT_HOT = 6,
};
static bool bq25890_is_adc_property(enum power_supply_property psp)
{
switch (psp) {
......@@ -422,6 +443,17 @@ static bool bq25890_is_adc_property(enum power_supply_property psp)
static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq);
static int bq25890_get_vbus_voltage(struct bq25890_device *bq)
{
int ret;
ret = bq25890_field_read(bq, F_VBUSV);
if (ret < 0)
return ret;
return bq25890_find_val(ret, TBL_VBUSV);
}
static int bq25890_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
......@@ -499,6 +531,18 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG);
/* When temperature is too low, charge current is decreased */
if (bq->state.ntc_fault == NTC_FAULT_COOL) {
ret = bq25890_field_read(bq, F_JEITA_ISET);
if (ret < 0)
return ret;
if (ret)
val->intval /= 5;
else
val->intval /= 2;
}
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
......@@ -528,11 +572,11 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq25890_field_read(bq, F_IILIM);
ret = bq25890_field_read(bq, F_IINLIM);
if (ret < 0)
return ret;
val->intval = bq25890_find_val(ret, TBL_IILIM);
val->intval = bq25890_find_val(ret, TBL_IINLIM);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
......@@ -569,6 +613,43 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
return 0;
}
/* On the BQ25892 try to get charger-type info from our supplier */
static void bq25890_charger_external_power_changed(struct power_supply *psy)
{
struct bq25890_device *bq = power_supply_get_drvdata(psy);
union power_supply_propval val;
int input_current_limit, ret;
if (bq->chip_version != BQ25892)
return;
ret = power_supply_get_property_from_supplier(bq->charger,
POWER_SUPPLY_PROP_USB_TYPE,
&val);
if (ret)
return;
switch (val.intval) {
case POWER_SUPPLY_USB_TYPE_DCP:
input_current_limit = bq25890_find_idx(2000000, TBL_IINLIM);
if (bq->pump_express_vbus_max) {
queue_delayed_work(system_power_efficient_wq,
&bq->pump_express_work,
PUMP_EXPRESS_START_DELAY);
}
break;
case POWER_SUPPLY_USB_TYPE_CDP:
case POWER_SUPPLY_USB_TYPE_ACA:
input_current_limit = bq25890_find_idx(1500000, TBL_IINLIM);
break;
case POWER_SUPPLY_USB_TYPE_SDP:
default:
input_current_limit = bq25890_find_idx(500000, TBL_IINLIM);
}
bq25890_field_write(bq, F_IINLIM, input_current_limit);
}
static int bq25890_get_chip_state(struct bq25890_device *bq,
struct bq25890_state *state)
{
......@@ -583,7 +664,8 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
{F_VSYS_STAT, &state->vsys_status},
{F_BOOST_FAULT, &state->boost_fault},
{F_BAT_FAULT, &state->bat_fault},
{F_CHG_FAULT, &state->chrg_fault}
{F_CHG_FAULT, &state->chrg_fault},
{F_NTC_FAULT, &state->ntc_fault}
};
for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
......@@ -594,9 +676,10 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
*state_fields[i].data = ret;
}
dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n",
dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n",
state->chrg_status, state->online, state->vsys_status,
state->chrg_fault, state->boost_fault, state->bat_fault);
state->chrg_fault, state->boost_fault, state->bat_fault,
state->ntc_fault);
return 0;
}
......@@ -670,33 +753,69 @@ static int bq25890_chip_reset(struct bq25890_device *bq)
return 0;
}
static int bq25890_hw_init(struct bq25890_device *bq)
static int bq25890_rw_init_data(struct bq25890_device *bq)
{
bool write = !bq->read_back_init_data;
int ret;
int i;
const struct {
enum bq25890_fields id;
u32 value;
u8 *value;
} init_data[] = {
{F_ICHG, bq->init_data.ichg},
{F_VREG, bq->init_data.vreg},
{F_ITERM, bq->init_data.iterm},
{F_IPRECHG, bq->init_data.iprechg},
{F_SYSVMIN, bq->init_data.sysvmin},
{F_BOOSTV, bq->init_data.boostv},
{F_BOOSTI, bq->init_data.boosti},
{F_BOOSTF, bq->init_data.boostf},
{F_EN_ILIM, bq->init_data.ilim_en},
{F_TREG, bq->init_data.treg},
{F_BATCMP, bq->init_data.rbatcomp},
{F_VCLAMP, bq->init_data.vclamp},
{F_ICHG, &bq->init_data.ichg},
{F_VREG, &bq->init_data.vreg},
{F_ITERM, &bq->init_data.iterm},
{F_IPRECHG, &bq->init_data.iprechg},
{F_SYSVMIN, &bq->init_data.sysvmin},
{F_BOOSTV, &bq->init_data.boostv},
{F_BOOSTI, &bq->init_data.boosti},
{F_BOOSTF, &bq->init_data.boostf},
{F_EN_ILIM, &bq->init_data.ilim_en},
{F_TREG, &bq->init_data.treg},
{F_BATCMP, &bq->init_data.rbatcomp},
{F_VCLAMP, &bq->init_data.vclamp},
};
ret = bq25890_chip_reset(bq);
if (ret < 0) {
dev_dbg(bq->dev, "Reset failed %d\n", ret);
return ret;
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
if (write) {
ret = bq25890_field_write(bq, init_data[i].id,
*init_data[i].value);
} else {
ret = bq25890_field_read(bq, init_data[i].id);
if (ret >= 0)
*init_data[i].value = ret;
}
if (ret < 0) {
dev_dbg(bq->dev, "Accessing init data failed %d\n", ret);
return ret;
}
}
return 0;
}
static int bq25890_hw_init(struct bq25890_device *bq)
{
int ret;
if (!bq->skip_reset) {
ret = bq25890_chip_reset(bq);
if (ret < 0) {
dev_dbg(bq->dev, "Reset failed %d\n", ret);
return ret;
}
} else {
/*
* Ensure charging is enabled, on some boards where the fw
* takes care of initalizition F_CHG_CFG is set to 0 before
* handing control over to the OS.
*/
ret = bq25890_field_write(bq, F_CHG_CFG, 1);
if (ret < 0) {
dev_dbg(bq->dev, "Enabling charging failed %d\n", ret);
return ret;
}
}
/* disable watchdog */
......@@ -707,14 +826,9 @@ static int bq25890_hw_init(struct bq25890_device *bq)
}
/* initialize currents/voltages and other parameters */
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
ret = bq25890_field_write(bq, init_data[i].id,
init_data[i].value);
if (ret < 0) {
dev_dbg(bq->dev, "Writing init data failed %d\n", ret);
return ret;
}
}
ret = bq25890_rw_init_data(bq);
if (ret)
return ret;
ret = bq25890_get_chip_state(bq, &bq->state);
if (ret < 0) {
......@@ -760,6 +874,7 @@ static const struct power_supply_desc bq25890_power_supply_desc = {
.properties = bq25890_power_supply_props,
.num_properties = ARRAY_SIZE(bq25890_power_supply_props),
.get_property = bq25890_power_supply_get_property,
.external_power_changed = bq25890_charger_external_power_changed,
};
static int bq25890_power_supply_init(struct bq25890_device *bq)
......@@ -776,6 +891,64 @@ static int bq25890_power_supply_init(struct bq25890_device *bq)
return PTR_ERR_OR_ZERO(bq->charger);
}
static int bq25890_set_otg_cfg(struct bq25890_device *bq, u8 val)
{
int ret;
ret = bq25890_field_write(bq, F_OTG_CFG, val);
if (ret < 0)
dev_err(bq->dev, "Error switching to boost/charger mode: %d\n", ret);
return ret;
}
static void bq25890_pump_express_work(struct work_struct *data)
{
struct bq25890_device *bq =
container_of(data, struct bq25890_device, pump_express_work.work);
int voltage, i, ret;
dev_dbg(bq->dev, "Start to request input voltage increasing\n");
/* Enable current pulse voltage control protocol */
ret = bq25890_field_write(bq, F_PUMPX_EN, 1);
if (ret < 0)
goto error_print;
for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {
voltage = bq25890_get_vbus_voltage(bq);
if (voltage < 0)
goto error_print;
dev_dbg(bq->dev, "input voltage = %d uV\n", voltage);
if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) >
bq->pump_express_vbus_max)
break;
ret = bq25890_field_write(bq, F_PUMPX_UP, 1);
if (ret < 0)
goto error_print;
/* Note a single PUMPX up pulse-sequence takes 2.1s */
ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP],
ret, !ret, 100000, 3000000);
if (ret < 0)
goto error_print;
/* Make sure ADC has sampled Vbus before checking again */
msleep(1000);
}
bq25890_field_write(bq, F_PUMPX_EN, 0);
dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
voltage);
return;
error_print:
dev_err(bq->dev, "Failed to request hi-voltage charging\n");
}
static void bq25890_usb_work(struct work_struct *data)
{
int ret;
......@@ -785,25 +958,16 @@ static void bq25890_usb_work(struct work_struct *data)
switch (bq->usb_event) {
case USB_EVENT_ID:
/* Enable boost mode */
ret = bq25890_field_write(bq, F_OTG_CFG, 1);
if (ret < 0)
goto error;
bq25890_set_otg_cfg(bq, 1);
break;
case USB_EVENT_NONE:
/* Disable boost mode */
ret = bq25890_field_write(bq, F_OTG_CFG, 0);
if (ret < 0)
goto error;
power_supply_changed(bq->charger);
ret = bq25890_set_otg_cfg(bq, 0);
if (ret == 0)
power_supply_changed(bq->charger);
break;
}
return;
error:
dev_err(bq->dev, "Error switching to boost/charger mode.\n");
}
static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
......@@ -818,6 +982,45 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
return NOTIFY_OK;
}
#ifdef CONFIG_REGULATOR
static int bq25890_vbus_enable(struct regulator_dev *rdev)
{
struct bq25890_device *bq = rdev_get_drvdata(rdev);
return bq25890_set_otg_cfg(bq, 1);
}
static int bq25890_vbus_disable(struct regulator_dev *rdev)
{
struct bq25890_device *bq = rdev_get_drvdata(rdev);
return bq25890_set_otg_cfg(bq, 0);
}
static int bq25890_vbus_is_enabled(struct regulator_dev *rdev)
{
struct bq25890_device *bq = rdev_get_drvdata(rdev);
return bq25890_field_read(bq, F_OTG_CFG);
}
static const struct regulator_ops bq25890_vbus_ops = {
.enable = bq25890_vbus_enable,
.disable = bq25890_vbus_disable,
.is_enabled = bq25890_vbus_is_enabled,
};
static const struct regulator_desc bq25890_vbus_desc = {
.name = "usb_otg_vbus",
.of_match = "usb-otg-vbus",
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &bq25890_vbus_ops,
.fixed_uV = 5000000,
.n_voltages = 1,
};
#endif
static int bq25890_get_chip_version(struct bq25890_device *bq)
{
int id, rev;
......@@ -936,6 +1139,16 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
int ret;
struct bq25890_init_data *init = &bq->init_data;
/* Optional, left at 0 if property is not present */
device_property_read_u32(bq->dev, "linux,pump-express-vbus-max",
&bq->pump_express_vbus_max);
bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset");
bq->read_back_init_data = device_property_read_bool(bq->dev,
"linux,read-back-settings");
if (bq->read_back_init_data)
return 0;
ret = bq25890_fw_read_u32_props(bq);
if (ret < 0)
return ret;
......@@ -952,7 +1165,6 @@ static int bq25890_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct bq25890_device *bq;
int ret;
int i;
bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
if (!bq)
......@@ -962,21 +1174,17 @@ static int bq25890_probe(struct i2c_client *client,
bq->dev = dev;
mutex_init(&bq->lock);
INIT_DELAYED_WORK(&bq->pump_express_work, bq25890_pump_express_work);
bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
if (IS_ERR(bq->rmap))
return dev_err_probe(dev, PTR_ERR(bq->rmap),
"failed to allocate register map\n");
for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
const struct reg_field *reg_fields = bq25890_reg_fields;
bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
reg_fields[i]);
if (IS_ERR(bq->rmap_fields[i]))
return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]),
"cannot allocate regmap field\n");
}
ret = devm_regmap_field_bulk_alloc(dev, bq->rmap, bq->rmap_fields,
bq25890_reg_fields, F_MAX_FIELDS);
if (ret)
return ret;
i2c_set_clientdata(client, bq);
......@@ -986,16 +1194,9 @@ static int bq25890_probe(struct i2c_client *client,
return ret;
}
if (!dev->platform_data) {
ret = bq25890_fw_probe(bq);
if (ret < 0) {
dev_err(dev, "Cannot read device properties: %d\n",
ret);
return ret;
}
} else {
return -ENODEV;
}
ret = bq25890_fw_probe(bq);
if (ret < 0)
return dev_err_probe(dev, ret, "reading device properties\n");
ret = bq25890_hw_init(bq);
if (ret < 0) {
......@@ -1018,6 +1219,22 @@ static int bq25890_probe(struct i2c_client *client,
bq->usb_nb.notifier_call = bq25890_usb_notifier;
usb_register_notifier(bq->usb_phy, &bq->usb_nb);
}
#ifdef CONFIG_REGULATOR
else {
struct bq25890_platform_data *pdata = dev_get_platdata(dev);
struct regulator_config cfg = { };
struct regulator_dev *reg;
cfg.dev = dev;
cfg.driver_data = bq;
if (pdata)
cfg.init_data = pdata->regulator_init_data;
reg = devm_regulator_register(dev, &bq25890_vbus_desc, &cfg);
if (IS_ERR(reg))
return dev_err_probe(dev, PTR_ERR(reg), "registering regulator");
}
#endif
ret = bq25890_power_supply_init(bq);
if (ret < 0) {
......@@ -1048,12 +1265,36 @@ static int bq25890_remove(struct i2c_client *client)
if (!IS_ERR_OR_NULL(bq->usb_phy))
usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
/* reset all registers to default values */
bq25890_chip_reset(bq);
if (!bq->skip_reset) {
/* reset all registers to default values */
bq25890_chip_reset(bq);
}
return 0;
}
static void bq25890_shutdown(struct i2c_client *client)
{
struct bq25890_device *bq = i2c_get_clientdata(client);
/*
* TODO this if + return should probably be removed, but that would
* introduce a function change for boards using the usb-phy framework.
* This needs to be tested on such a board before making this change.
*/
if (!IS_ERR_OR_NULL(bq->usb_phy))
return;
/*
* Turn off the 5v Boost regulator which outputs Vbus to the device's
* Micro-USB or Type-C USB port. Leaving this on drains power and
* this avoids the PMIC on some device-models seeing this as Vbus
* getting inserted after shutdown, causing the device to immediately
* power-up again.
*/
bq25890_set_otg_cfg(bq, 0);
}
#ifdef CONFIG_PM_SLEEP
static int bq25890_suspend(struct device *dev)
{
......@@ -1133,6 +1374,7 @@ static struct i2c_driver bq25890_driver = {
},
.probe = bq25890_probe,
.remove = bq25890_remove,
.shutdown = bq25890_shutdown,
.id_table = bq25890_i2c_ids,
};
module_i2c_driver(bq25890_driver);
......
......@@ -376,46 +376,49 @@ int power_supply_is_system_supplied(void)
}
EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
static int __power_supply_get_supplier_max_current(struct device *dev,
void *data)
struct psy_get_supplier_prop_data {
struct power_supply *psy;
enum power_supply_property psp;
union power_supply_propval *val;
};
static int __power_supply_get_supplier_property(struct device *dev, void *_data)
{
union power_supply_propval ret = {0,};
struct power_supply *epsy = dev_get_drvdata(dev);
struct power_supply *psy = data;
struct psy_get_supplier_prop_data *data = _data;
if (__power_supply_is_supplied_by(epsy, psy))
if (!epsy->desc->get_property(epsy,
POWER_SUPPLY_PROP_CURRENT_MAX,
&ret))
return ret.intval;
if (__power_supply_is_supplied_by(epsy, data->psy))
if (!epsy->desc->get_property(epsy, data->psp, data->val))
return 1; /* Success */
return 0;
return 0; /* Continue iterating */
}
int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
int power_supply_get_property_from_supplier(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
union power_supply_propval val = {0,};
int curr;
if (!psy->desc->set_property)
return -EINVAL;
struct psy_get_supplier_prop_data data = {
.psy = psy,
.psp = psp,
.val = val,
};
int ret;
/*
* This function is not intended for use with a supply with multiple
* suppliers, we simply pick the first supply to report a non 0
* max-current.
* suppliers, we simply pick the first supply to report the psp.
*/
curr = class_for_each_device(power_supply_class, NULL, psy,
__power_supply_get_supplier_max_current);
if (curr <= 0)
return (curr == 0) ? -ENODEV : curr;
val.intval = curr;
ret = class_for_each_device(power_supply_class, NULL, &data,
__power_supply_get_supplier_property);
if (ret < 0)
return ret;
if (ret == 0)
return -ENODEV;
return psy->desc->set_property(psy,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
return 0;
}
EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
EXPORT_SYMBOL_GPL(power_supply_get_property_from_supplier);
int power_supply_set_battery_charged(struct power_supply *psy)
{
......
......@@ -13,6 +13,13 @@
#include <linux/regmap.h>
enum intel_cht_wc_models {
INTEL_CHT_WC_UNKNOWN,
INTEL_CHT_WC_GPD_WIN_POCKET,
INTEL_CHT_WC_XIAOMI_MIPAD2,
INTEL_CHT_WC_LENOVO_YOGABOOK1,
};
/**
* struct intel_soc_pmic - Intel SoC PMIC data
* @irq: Master interrupt number of the parent PMIC device
......@@ -39,6 +46,7 @@ struct intel_soc_pmic {
struct regmap_irq_chip_data *irq_chip_data_crit;
struct device *dev;
struct intel_scu_ipc_dev *scu;
enum intel_cht_wc_models cht_wc_model;
};
int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Platform data for the TI bq25890 battery charger driver.
*/
#ifndef _BQ25890_CHARGER_H_
#define _BQ25890_CHARGER_H_
struct regulator_init_data;
struct bq25890_platform_data {
const struct regulator_init_data *regulator_init_data;
};
#endif
......@@ -597,8 +597,9 @@ power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table
int table_len, int temp);
extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_input_current_limit_from_supplier(
struct power_supply *psy);
int power_supply_get_property_from_supplier(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val);
extern int power_supply_set_battery_charged(struct power_supply *psy);
#ifdef CONFIG_POWER_SUPPLY
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment