diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-05 12:28:15 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-05 12:28:15 -0800 |
commit | 400c5bd5a5b1faf3089322ace58b974446a8ddc3 (patch) | |
tree | 78594fc5e426c68b322adc11bd62a417267811eb /drivers/power | |
parent | 9bd9fa6c147e68fc4dc3b35893979720ba7d0321 (diff) | |
parent | 6bd03ce3c12a22d86f59070f1da15aaa2bde8a51 (diff) |
Merge tag 'for-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
- new AXP20X USB Power driver
- new Qualcomm SMBB driver
- new TPS65217 Charger driver
- BQ24257: add BQ24250/BQ24251 support
- overhaul bq27x00 battery driver, rename to bq27xxx
- misc fixes and cleanups
* tag 'for-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (54 commits)
power: bq27xxx_battery: Remove unneeded dependency in Kconfig
power: bq27xxx_battery: move irq handler to i2c section
power: bq27xxx_battery: fix platform probe
twl4030_charger: add missing iio dependency
power_supply: charger-manager: add missing of_node_put
Documentation: power: bq24257: Document exported sysfs entries
power: bq24257: Add various device-specific sysfs properties
power: bq24257: Allow input current limit sysfs access
power: bq24257: Add input DPM voltage threshold setting support
power: bq24257: Add over voltage protection setting support
power: bq24257: Add SW-based approach for Power Good determination
power: bq24257: Allow manual setting of input current limit
power: bq24257: Add bit definition for temp sense enable
power: bq24257: Add basic support for bq24250/bq24251
dt: power: bq24257-charger: Cover additional devices
power: bq24257: Simplify bq24257_power_supply_init()
power: bq24257: Use managed power supply register
power: bq24257: Streamline input current limit setup
power: bq24257: Remove IRQ config through stat-gpios
power: bq27xxx_battery: fix signedness bug in bq27xxx_battery_read_health()
...
Diffstat (limited to 'drivers/power')
25 files changed, 3467 insertions, 1440 deletions
diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c index d49579b227ec..63c57dc82ac1 100644 --- a/drivers/power/88pm860x_battery.c +++ b/drivers/power/88pm860x_battery.c @@ -954,47 +954,33 @@ static int pm860x_battery_probe(struct platform_device *pdev) else info->resistor = 300; /* set default internal resistor */ - info->battery = power_supply_register(&pdev->dev, &pm860x_battery_desc, - NULL); + info->battery = devm_power_supply_register(&pdev->dev, + &pm860x_battery_desc, + NULL); if (IS_ERR(info->battery)) return PTR_ERR(info->battery); info->battery->dev.parent = &pdev->dev; - ret = request_threaded_irq(info->irq_cc, NULL, - pm860x_coulomb_handler, IRQF_ONESHOT, - "coulomb", info); + ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL, + pm860x_coulomb_handler, IRQF_ONESHOT, + "coulomb", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq_cc, ret); - goto out_reg; + return ret; } - ret = request_threaded_irq(info->irq_batt, NULL, pm860x_batt_handler, - IRQF_ONESHOT, "battery", info); + ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL, + pm860x_batt_handler, + IRQF_ONESHOT, "battery", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq_batt, ret); - goto out_coulomb; + return ret; } return 0; - -out_coulomb: - free_irq(info->irq_cc, info); -out_reg: - power_supply_unregister(info->battery); - return ret; -} - -static int pm860x_battery_remove(struct platform_device *pdev) -{ - struct pm860x_battery_info *info = platform_get_drvdata(pdev); - - free_irq(info->irq_batt, info); - free_irq(info->irq_cc, info); - power_supply_unregister(info->battery); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1028,7 +1014,6 @@ static struct platform_driver pm860x_battery_driver = { .pm = &pm860x_battery_pm_ops, }, .probe = pm860x_battery_probe, - .remove = pm860x_battery_remove, }; module_platform_driver(pm860x_battery_driver); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index f8758d6febf8..02b3b313809a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -157,26 +157,25 @@ config BATTERY_SBS Say Y to include support for SBS battery driver for SBS-compliant gas gauges. -config BATTERY_BQ27x00 - tristate "BQ27x00 battery driver" - depends on I2C || I2C=n +config BATTERY_BQ27XXX + tristate "BQ27xxx battery driver" help - Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips. + Say Y here to enable support for batteries with BQ27xxx (I2C/HDQ) chips. -config BATTERY_BQ27X00_I2C - bool "BQ27200/BQ27500 support" - depends on BATTERY_BQ27x00 +config BATTERY_BQ27XXX_I2C + bool "BQ27xxx I2C support" + depends on BATTERY_BQ27XXX depends on I2C default y help - Say Y here to enable support for batteries with BQ27x00 (I2C) chips. + Say Y here to enable support for batteries with BQ27xxx (I2C) chips. -config BATTERY_BQ27X00_PLATFORM - bool "BQ27000 support" - depends on BATTERY_BQ27x00 +config BATTERY_BQ27XXX_PLATFORM + bool "BQ27xxx HDQ support" + depends on BATTERY_BQ27XXX default y help - Say Y here to enable support for batteries with BQ27000 (HDQ) chips. + Say Y here to enable support for batteries with BQ27xxx (HDQ) chips. config BATTERY_DA9030 tristate "DA9030 battery driver" @@ -313,7 +312,7 @@ config CHARGER_MAX8903 config CHARGER_TWL4030 tristate "OMAP TWL4030 BCI charger driver" - depends on TWL4030_CORE + depends on IIO && TWL4030_CORE help Say Y here to enable support for TWL4030 Battery Charge Interface. @@ -379,6 +378,18 @@ config CHARGER_MAX8998 Say Y to enable support for the battery charger control sysfs and platform data of MAX8998/LP3974 PMICs. +config CHARGER_QCOM_SMBB + tristate "Qualcomm Switch-Mode Battery Charger and Boost" + depends on MFD_SPMI_PMIC || COMPILE_TEST + depends on OF + help + Say Y to include support for the Switch-Mode Battery Charger and + Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger + is an integrated, single-cell lithium-ion battery charger. DT + configuration is required for loading, see the devicetree + documentation for more detail. The base name for this driver is + 'pm8941_charger'. + config CHARGER_BQ2415X tristate "TI BQ2415x battery charger driver" depends on I2C @@ -397,12 +408,13 @@ config CHARGER_BQ24190 Say Y to enable support for the TI BQ24190 battery charger. config CHARGER_BQ24257 - tristate "TI BQ24257 battery charger driver" + tristate "TI BQ24250/24251/24257 battery charger driver" depends on I2C depends on GPIOLIB || COMPILE_TEST depends on REGMAP_I2C help - Say Y to enable support for the TI BQ24257 battery charger. + Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery + chargers. config CHARGER_BQ24735 tristate "TI BQ24735 battery charger support" @@ -434,6 +446,13 @@ config CHARGER_TPS65090 Say Y here to enable support for battery charging with TPS65090 PMIC chips. +config CHARGER_TPS65217 + tristate "TPS65217 battery charger driver" + depends on MFD_TPS65217 + help + Say Y here to enable support for battery charging with TPS65217 + PMIC chips. + config BATTERY_GAUGE_LTC2941 tristate "LTC2941/LTC2943 Battery Gauge Driver" depends on I2C @@ -472,6 +491,13 @@ config CHARGER_RT9455 help Say Y to enable support for Richtek RT9455 battery charger. +config AXP20X_POWER + tristate "AXP20x power supply driver" + depends on MFD_AXP20X + help + This driver provides support for the power supply features of + AXP20x PMIC. + source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 5752ce818f51..b0e1bf190e3d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o obj-$(CONFIG_MAX8925_POWER) += max8925_power.o obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o obj-$(CONFIG_WM831X_POWER) += wm831x_power.o @@ -29,7 +30,7 @@ obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o -obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o +obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o @@ -57,6 +58,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o +obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o @@ -65,6 +67,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o +obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o obj-$(CONFIG_POWER_RESET) += reset/ obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c new file mode 100644 index 000000000000..421a90b83567 --- /dev/null +++ b/drivers/power/axp20x_usb_power.c @@ -0,0 +1,248 @@ +/* + * AXP20x PMIC USB power supply status driver + * + * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> + * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRVNAME "axp20x-usb-power-supply" + +#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) +#define AXP20X_PWR_STATUS_VBUS_USED BIT(4) + +#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) + +#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) +#define AXP20X_VBUS_CLIMIT_MASK 3 +#define AXP20X_VBUC_CLIMIT_900mA 0 +#define AXP20X_VBUC_CLIMIT_500mA 1 +#define AXP20X_VBUC_CLIMIT_100mA 2 +#define AXP20X_VBUC_CLIMIT_NONE 3 + +#define AXP20X_ADC_EN1_VBUS_CURR BIT(2) +#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) + +#define AXP20X_VBUS_MON_VBUS_VALID BIT(3) + +struct axp20x_usb_power { + struct regmap *regmap; + struct power_supply *supply; +}; + +static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) +{ + struct axp20x_usb_power *power = devid; + + power_supply_changed(power->supply); + + return IRQ_HANDLED; +} + +static int axp20x_usb_power_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct axp20x_usb_power *power = power_supply_get_drvdata(psy); + unsigned int input, v; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (ret) + return ret; + + val->intval = AXP20X_VBUS_VHOLD_uV(v); + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = axp20x_read_variable_width(power->regmap, + AXP20X_VBUS_V_ADC_H, 12); + if (ret < 0) + return ret; + + val->intval = ret * 1700; /* 1 step = 1.7 mV */ + return 0; + case POWER_SUPPLY_PROP_CURRENT_MAX: + ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); + if (ret) + return ret; + + switch (v & AXP20X_VBUS_CLIMIT_MASK) { + case AXP20X_VBUC_CLIMIT_100mA: + val->intval = 100000; + break; + case AXP20X_VBUC_CLIMIT_500mA: + val->intval = 500000; + break; + case AXP20X_VBUC_CLIMIT_900mA: + val->intval = 900000; + break; + case AXP20X_VBUC_CLIMIT_NONE: + val->intval = -1; + break; + } + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = axp20x_read_variable_width(power->regmap, + AXP20X_VBUS_I_ADC_H, 12); + if (ret < 0) + return ret; + + val->intval = ret * 375; /* 1 step = 0.375 mA */ + return 0; + default: + break; + } + + /* All the properties below need the input-status reg value */ + ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_HEALTH: + if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v); + if (ret) + return ret; + + if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) { + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + } + + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property axp20x_usb_power_properties[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static const struct power_supply_desc axp20x_usb_power_desc = { + .name = "axp20x-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = axp20x_usb_power_properties, + .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), + .get_property = axp20x_usb_power_get_property, +}; + +static int axp20x_usb_power_probe(struct platform_device *pdev) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + struct axp20x_usb_power *power; + static const char * const irq_names[] = { "VBUS_PLUGIN", + "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" }; + int i, irq, ret; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + if (!axp20x) { + dev_err(&pdev->dev, "Parent drvdata not set\n"); + return -EINVAL; + } + + power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->regmap = axp20x->regmap; + + /* Enable vbus valid checking */ + ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, + AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID); + if (ret) + return ret; + + /* Enable vbus voltage and current measurement */ + ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, + AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); + if (ret) + return ret; + + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = power; + + power->supply = devm_power_supply_register(&pdev->dev, + &axp20x_usb_power_desc, &psy_cfg); + if (IS_ERR(power->supply)) + return PTR_ERR(power->supply); + + /* Request irqs after registering, as irqs may trigger immediately */ + for (i = 0; i < ARRAY_SIZE(irq_names); i++) { + irq = platform_get_irq_byname(pdev, irq_names[i]); + if (irq < 0) { + dev_warn(&pdev->dev, "No IRQ for %s: %d\n", + irq_names[i], irq); + continue; + } + irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + ret = devm_request_any_context_irq(&pdev->dev, irq, + axp20x_usb_power_irq, 0, DRVNAME, power); + if (ret < 0) + dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", + irq_names[i], ret); + } + + return 0; +} + +static const struct of_device_id axp20x_usb_power_match[] = { + { .compatible = "x-powers,axp202-usb-power-supply" }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); + +static struct platform_driver axp20x_usb_power_driver = { + .probe = axp20x_usb_power_probe, + .driver = { + .name = DRVNAME, + .of_match_table = axp20x_usb_power_match, + }, +}; + +module_platform_driver(axp20x_usb_power_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index ec212b5be755..4afd76848bce 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1704,7 +1704,7 @@ error_4: error_3: bq2415x_power_supply_exit(bq); error_2: - if (bq->notify_node) + if (bq && bq->notify_node) of_node_put(bq->notify_node); kfree(name); error_1: diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index 469a452cbe10..f5746b9f4e83 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -1543,5 +1543,4 @@ module_i2c_driver(bq24190_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); -MODULE_ALIAS("i2c:bq24190-charger"); MODULE_DESCRIPTION("TI BQ24190 Charger Driver"); diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c index 5859bc7c1616..1fea2c7ef97f 100644 --- a/drivers/power/bq24257_charger.c +++ b/drivers/power/bq24257_charger.c @@ -13,6 +13,10 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * Datasheets: + * http://www.ti.com/product/bq24250 + * http://www.ti.com/product/bq24251 + * http://www.ti.com/product/bq24257 */ #include <linux/module.h> @@ -36,18 +40,33 @@ #define BQ24257_REG_7 0x06 #define BQ24257_MANUFACTURER "Texas Instruments" -#define BQ24257_STAT_IRQ "stat" #define BQ24257_PG_GPIO "pg" #define BQ24257_ILIM_SET_DELAY 1000 /* msec */ +/* + * When adding support for new devices make sure that enum bq2425x_chip and + * bq2425x_chip_name[] always stay in sync! + */ +enum bq2425x_chip { + BQ24250, + BQ24251, + BQ24257, +}; + +static const char *const bq2425x_chip_name[] = { + "bq24250", + "bq24251", + "bq24257", +}; + enum bq24257_fields { F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT, /* REG 1 */ F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE, /* REG 2 */ F_VBAT, F_USB_DET, /* REG 3 */ F_ICHG, F_ITERM, /* REG 4 */ F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */ - F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_STAT, /* REG 6 */ + F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_EN, F_TS_STAT, /* REG 6 */ F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM, /* REG 7 */ F_MAX_FIELDS @@ -58,6 +77,9 @@ struct bq24257_init_data { u8 ichg; /* charge current */ u8 vbat; /* regulation voltage */ u8 iterm; /* termination current */ + u8 iilimit; /* input current limit */ + u8 vovp; /* over voltage protection voltage */ + u8 vindpm; /* VDMP input threshold voltage */ }; struct bq24257_state { @@ -71,6 +93,8 @@ struct bq24257_device { struct device *dev; struct power_supply *charger; + enum bq2425x_chip chip; + struct regmap *rmap; struct regmap_field *rmap_fields[F_MAX_FIELDS]; @@ -82,6 +106,8 @@ struct bq24257_device { struct bq24257_state state; struct mutex lock; /* protect state data */ + + bool iilimit_autoset_enable; }; static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg) @@ -135,6 +161,7 @@ static const struct reg_field bq24257_reg_fields[] = { [F_X2_TMR_EN] = REG_FIELD(BQ24257_REG_6, 7, 7), [F_TMR] = REG_FIELD(BQ24257_REG_6, 5, 6), [F_SYSOFF] = REG_FIELD(BQ24257_REG_6, 4, 4), + [F_TS_EN] = REG_FIELD(BQ24257_REG_6, 3, 3), [F_TS_STAT] = REG_FIELD(BQ24257_REG_6, 0, 2), /* REG 7 */ [F_VOVP] = REG_FIELD(BQ24257_REG_7, 5, 7), @@ -169,6 +196,26 @@ static const u32 bq24257_iterm_map[] = { #define BQ24257_ITERM_MAP_SIZE ARRAY_SIZE(bq24257_iterm_map) +static const u32 bq24257_iilimit_map[] = { + 100000, 150000, 500000, 900000, 1500000, 2000000 +}; + +#define BQ24257_IILIMIT_MAP_SIZE ARRAY_SIZE(bq24257_iilimit_map) + +static const u32 bq24257_vovp_map[] = { + 6000000, 6500000, 7000000, 8000000, 9000000, 9500000, 10000000, + 10500000 +}; + +#define BQ24257_VOVP_MAP_SIZE ARRAY_SIZE(bq24257_vovp_map) + +static const u32 bq24257_vindpm_map[] = { + 4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000, + 4760000 +}; + +#define BQ24257_VINDPM_MAP_SIZE ARRAY_SIZE(bq24257_vindpm_map) + static int bq24257_field_read(struct bq24257_device *bq, enum bq24257_fields field_id) { @@ -220,6 +267,47 @@ enum bq24257_fault { FAULT_INPUT_LDO_LOW, }; +static int bq24257_get_input_current_limit(struct bq24257_device *bq, + union power_supply_propval *val) +{ + int ret; + + ret = bq24257_field_read(bq, F_IILIMIT); + if (ret < 0) + return ret; + + /* + * The "External ILIM" and "Production & Test" modes are not exposed + * through this driver and not being covered by the lookup table. + * Should such a mode have become active let's return an error rather + * than exceeding the bounds of the lookup table and returning + * garbage. + */ + if (ret >= BQ24257_IILIMIT_MAP_SIZE) + return -ENODATA; + + val->intval = bq24257_iilimit_map[ret]; + + return 0; +} + +static int bq24257_set_input_current_limit(struct bq24257_device *bq, + const union power_supply_propval *val) +{ + /* + * Address the case where the user manually sets an input current limit + * while the charger auto-detection mechanism is is active. In this + * case we want to abort and go straight to the user-specified value. + */ + if (bq->iilimit_autoset_enable) + cancel_delayed_work_sync(&bq->iilimit_setup_work); + + return bq24257_field_write(bq, F_IILIMIT, + bq24257_find_idx(val->intval, + bq24257_iilimit_map, + BQ24257_IILIMIT_MAP_SIZE)); +} + static int bq24257_power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -249,6 +337,10 @@ static int bq24257_power_supply_get_property(struct power_supply *psy, val->strval = BQ24257_MANUFACTURER; break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq2425x_chip_name[bq->chip]; + break; + case POWER_SUPPLY_PROP_ONLINE: val->intval = state.power_good; break; @@ -300,6 +392,9 @@ static int bq24257_power_supply_get_property(struct power_supply *psy, val->intval = bq24257_iterm_map[bq->init_data.iterm]; break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return bq24257_get_input_current_limit(bq, val); + default: return -EINVAL; } @@ -307,6 +402,31 @@ static int bq24257_power_supply_get_property(struct power_supply *psy, return 0; } +static int bq24257_power_supply_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq24257_device *bq = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return bq24257_set_input_current_limit(bq, val); + default: + return -EINVAL; + } +} + +static int bq24257_power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return true; + default: + return false; + } +} + static int bq24257_get_chip_state(struct bq24257_device *bq, struct bq24257_state *state) { @@ -324,7 +444,26 @@ static int bq24257_get_chip_state(struct bq24257_device *bq, state->fault = ret; - state->power_good = !gpiod_get_value_cansleep(bq->pg); + if (bq->pg) + state->power_good = !gpiod_get_value_cansleep(bq->pg); + else + /* + * If we have a chip without a dedicated power-good GPIO or + * some other explicit bit that would provide this information + * assume the power is good if there is no supply related + * fault - and not good otherwise. There is a possibility for + * other errors to mask that power in fact is not good but this + * is probably the best we can do here. + */ + switch (state->fault) { + case FAULT_INPUT_OVP: + case FAULT_INPUT_UVLO: + case FAULT_INPUT_LDO_LOW: + state->power_good = false; + break; + default: + state->power_good = true; + } return 0; } @@ -361,6 +500,28 @@ enum bq24257_in_ilimit { IILIMIT_NONE, }; +enum bq24257_vovp { + VOVP_6000, + VOVP_6500, + VOVP_7000, + VOVP_8000, + VOVP_9000, + VOVP_9500, + VOVP_10000, + VOVP_10500 +}; + +enum bq24257_vindpm { + VINDPM_4200, + VINDPM_4280, + VINDPM_4360, + VINDPM_4440, + VINDPM_4520, + VINDPM_4600, + VINDPM_4680, + VINDPM_4760 +}; + enum bq24257_port_type { PORT_TYPE_DCP, /* Dedicated Charging Port */ PORT_TYPE_CDP, /* Charging Downstream Port */ @@ -449,41 +610,43 @@ static void bq24257_handle_state_change(struct bq24257_device *bq, { int ret; struct bq24257_state old_state; - bool reset_iilimit = false; - bool config_iilimit = false; mutex_lock(&bq->lock); old_state = bq->state; mutex_unlock(&bq->lock); - if (!new_state->power_good) { /* power removed */ - cancel_delayed_work_sync(&bq->iilimit_setup_work); - - /* activate D+/D- port detection algorithm */ - ret = bq24257_field_write(bq, F_DPDM_EN, 1); + /* + * Handle BQ2425x state changes observing whether the D+/D- based input + * current limit autoset functionality is enabled. + */ + if (!new_state->power_good) { + dev_dbg(bq->dev, "Power removed\n"); + if (bq->iilimit_autoset_enable) { + cancel_delayed_work_sync(&bq->iilimit_setup_work); + + /* activate D+/D- port detection algorithm */ + ret = bq24257_field_write(bq, F_DPDM_EN, 1); + if (ret < 0) + goto error; + } + /* + * When power is removed always return to the default input + * current limit as configured during probe. + */ + ret = bq24257_field_write(bq, F_IILIMIT, bq->init_data.iilimit); if (ret < 0) goto error; + } else if (!old_state.power_good) { + dev_dbg(bq->dev, "Power inserted\n"); - reset_iilimit = true; - } else if (!old_state.power_good) { /* power inserted */ - config_iilimit = true; - } else if (new_state->fault == FAULT_NO_BAT) { /* battery removed */ - cancel_delayed_work_sync(&bq->iilimit_setup_work); - - reset_iilimit = true; - } else if (old_state.fault == FAULT_NO_BAT) { /* battery connected */ - config_iilimit = true; - } else if (new_state->fault == FAULT_TIMER) { /* safety timer expired */ - dev_err(bq->dev, "Safety timer expired! Battery dead?\n"); - } - - if (reset_iilimit) { - ret = bq24257_field_write(bq, F_IILIMIT, IILIMIT_500); - if (ret < 0) - goto error; - } else if (config_iilimit) { - schedule_delayed_work(&bq->iilimit_setup_work, + if (bq->iilimit_autoset_enable) + /* configure input current limit */ + schedule_delayed_work(&bq->iilimit_setup_work, msecs_to_jiffies(BQ24257_ILIM_SET_DELAY)); + } else if (new_state->fault == FAULT_NO_BAT) { + dev_warn(bq->dev, "Battery removed\n"); + } else if (new_state->fault == FAULT_TIMER) { + dev_err(bq->dev, "Safety timer expired! Battery dead?\n"); } return; @@ -531,7 +694,9 @@ static int bq24257_hw_init(struct bq24257_device *bq) } init_data[] = { {F_ICHG, bq->init_data.ichg}, {F_VBAT, bq->init_data.vbat}, - {F_ITERM, bq->init_data.iterm} + {F_ITERM, bq->init_data.iterm}, + {F_VOVP, bq->init_data.vovp}, + {F_VINDPM, bq->init_data.vindpm}, }; /* @@ -558,7 +723,16 @@ static int bq24257_hw_init(struct bq24257_device *bq) bq->state = state; mutex_unlock(&bq->lock); - if (!state.power_good) + if (!bq->iilimit_autoset_enable) { + dev_dbg(bq->dev, "manually setting iilimit = %u\n", + bq->init_data.iilimit); + + /* program fixed input current limit */ + ret = bq24257_field_write(bq, F_IILIMIT, + bq->init_data.iilimit); + if (ret < 0) + return ret; + } else if (!state.power_good) /* activate D+/D- detection algorithm */ ret = bq24257_field_write(bq, F_DPDM_EN, 1); else if (state.fault != FAULT_NO_BAT) @@ -569,6 +743,7 @@ static int bq24257_hw_init(struct bq24257_device *bq) |