diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-09 14:44:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-09 14:44:39 -0700 |
commit | 0ce5c79f384b9f730cf03a1e464673ae906e7c89 (patch) | |
tree | 138c18ff2e9f6a01d7eadbca4b73719c4030948a | |
parent | 7151202b64c8c5eb163e41fa0adcb8239eea64aa (diff) | |
parent | 5d01fd38a3b01ca2112a689d70f7ecec40d952ee (diff) | |
download | linux-0ce5c79f384b9f730cf03a1e464673ae906e7c89.tar.gz linux-0ce5c79f384b9f730cf03a1e464673ae906e7c89.tar.bz2 linux-0ce5c79f384b9f730cf03a1e464673ae906e7c89.zip |
Merge tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset changes from Sebastian Reichel:
"New chip/feature support:
- bq27xxx: support updating battery config from DT
- bq24190: support loading battery charge info from DT
- LTC2941: add LTC2942/LTC2944 support
- max17042: add ACPI support
- max1721x: new driver
Misc:
- Move bq27xxx w1 driver from w1 into power-supply subsystem
- Introduce power_supply_set_input_current_limit_from_supplier
- constify stuff
- some minor fixes"
* tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (39 commits)
power: supply: bq27xxx: enable writing capacity values for bq27421
power: supply: bq24190_charger: Get input_current_limit from our supplier
power: supply: bq24190_charger: Export 5V boost converter as regulator
power: supply: bq24190_charger: Add power_supply_battery_info support
power: supply: bq24190_charger: Add property system-minimum-microvolt
power: supply: bq24190_charger: Enable devicetree config
dt-bindings: power: supply: Add docs for TI BQ24190 battery charger
power: supply: bq27xxx: Remove duplicate chip data arrays
power: supply: bq27xxx: Enable data memory update for certain chips
power: supply: bq27xxx: Add chip IDs for previously shadowed chips
power: supply: bq27xxx: Create single chip data table
power: supply: bq24190_charger: Add ti,bq24192i to devicetree table
power: supply: bq24190_charger: Add input_current_limit property
power: supply: Add power_supply_set_input_current_limit_from_supplier helper
power: supply: max17042_battery: Fix compiler warning
power: supply: core: Delete two error messages for a failed memory allocation in power_supply_check_supplies()
power: supply: make device_attribute const
power: supply: max17042_battery: Fix ACPI interrupt issues
power: supply: max17042_battery: Add support for ACPI enumeration
power: supply: lp8788: Make several arrays static const * const
...
27 files changed, 1471 insertions, 638 deletions
diff --git a/Documentation/devicetree/bindings/power/supply/bq24190.txt b/Documentation/devicetree/bindings/power/supply/bq24190.txt new file mode 100644 index 000000000000..9e517d307070 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/bq24190.txt @@ -0,0 +1,51 @@ +TI BQ24190 Li-Ion Battery Charger + +Required properties: +- compatible: contains one of the following: + * "ti,bq24190" + * "ti,bq24192i" +- reg: integer, I2C address of the charger. +- interrupts[-extended]: configuration for charger INT pin. + +Optional properties: +- monitored-battery: phandle of battery characteristics devicetree node + The charger uses the following battery properties: + + precharge-current-microamp: maximum charge current during precharge + phase (typically 20% of battery capacity). + + charge-term-current-microamp: a charge cycle terminates when the + battery voltage is above recharge threshold, and the current is below + this setting (typically 10% of battery capacity). + See also Documentation/devicetree/bindings/power/supply/battery.txt +- ti,system-minimum-microvolt: when power is connected and the battery is below + minimum system voltage, the system will be regulated above this setting. + +Notes: +- Some circuit boards wire the chip's "OTG" pin high (enabling 500mA default + charge current on USB SDP ports, among other features). To simulate this on + boards that wire the pin to a GPIO, set a gpio-hog. + +Example: + + bat: battery { + compatible = "simple-battery"; + precharge-current-microamp = <256000>; + charge-term-current-microamp = <128000>; + // etc. + }; + + bq24190: charger@6a { + compatible = "ti,bq24190"; + reg = <0x6a>; + interrupts-extended = <&gpiochip 10 IRQ_TYPE_EDGE_FALLING>; + monitored-battery = <&bat>; + ti,system-minimum-microvolt = <3200000>; + }; + + &twl_gpio { + otg { + gpio-hog; + gpios = <6 0>; + output-high; + line-name = "otg-gpio"; + }; + }; diff --git a/Documentation/devicetree/bindings/power/supply/ltc2941.txt b/Documentation/devicetree/bindings/power/supply/ltc2941.txt index a9d7aa60558b..3b9ba147b041 100644 --- a/Documentation/devicetree/bindings/power/supply/ltc2941.txt +++ b/Documentation/devicetree/bindings/power/supply/ltc2941.txt @@ -1,13 +1,14 @@ -binding for LTC2941 and LTC2943 battery gauges +binding for LTC2941, LTC2942, LTC2943 and LTC2944 battery gauges -Both the LTC2941 and LTC2943 measure battery capacity. -The LTC2943 is compatible with the LTC2941, it adds voltage and -temperature monitoring, and uses a slightly different conversion -formula for the charge counter. +All chips measure battery capacity. +The LTC2942 is pin compatible with the LTC2941, it adds voltage and +temperature monitoring, and is runtime detected. LTC2943 and LTC2944 +is software compatible, uses a slightly different conversion formula +for the charge counter and adds voltage, current and temperature monitoring. Required properties: -- compatible: Should contain "lltc,ltc2941" or "lltc,ltc2943" which also - indicates the type of I2C chip attached. +- compatible: Should contain "lltc,ltc2941", "lltc,ltc2942", "lltc,ltc2943" + or "lltc,ltc2944" which also indicates the type of I2C chip attached. - reg: The 7-bit I2C address. - lltc,resistor-sense: The sense resistor value in milli-ohms. Can be a 32-bit negative value when the battery has been connected to the wrong end of the diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 55fce8b75245..31080c254124 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -171,8 +171,8 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev, for_each_child_of_node(np, cnp) { if (of_property_read_u32(cnp, "reg", &wk_input)) { - dev_warn(&pdev->dev, "reg property is missing for %s\n", - cnp->full_name); + dev_warn(&pdev->dev, "reg property is missing for %pOF\n", + cnp); continue; } diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 969f5005669c..5ab90c1f3f7c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -198,6 +198,15 @@ config BATTERY_BQ27XXX_I2C Say Y here to enable support for batteries with BQ27xxx chips connected over an I2C bus. +config BATTERY_BQ27XXX_HDQ + tristate "BQ27xxx HDQ support" + depends on BATTERY_BQ27XXX + depends on W1 + default y + help + Say Y here to enable support for batteries with BQ27xxx chips + connected over an HDQ bus. + config BATTERY_BQ27XXX_DT_UPDATES_NVM bool "BQ27xxx support for update of NVM/flash data memory" depends on BATTERY_BQ27XXX_I2C @@ -313,6 +322,19 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. +config BATTERY_MAX1721X + tristate "MAX17211/MAX17215 standalone gas-gauge" + depends on W1 + select REGMAP_W1 + help + MAX1721x is fuel-gauge systems for lithium-ion (Li+) batteries + in handheld and portable equipment. MAX17211 used with single cell + battery. MAX17215 designed for muticell battery. Both them have + OneWire (W1) host interface. + + Say Y here to enable support for the MAX17211/MAX17215 standalone + battery gas-gauge. + config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 @@ -365,6 +387,7 @@ config BATTERY_RX51 config CHARGER_CPCAP tristate "CPCAP PMIC Charger Driver" depends on MFD_CPCAP && IIO + depends on OMAP_USB2 || (!OMAP_USB2 && COMPILE_TEST) default MFD_CPCAP help Say Y to enable support for CPCAP PMIC charger driver for Motorola diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a41f40957847..621a19058fec 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -38,12 +38,14 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o +obj-$(CONFIG_BATTERY_BQ27XXX_HDQ) += bq27xxx_battery_hdq.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o +obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c index d1eb2e359532..8e117b31ba79 100644 --- a/drivers/power/supply/act8945a_charger.c +++ b/drivers/power/supply/act8945a_charger.c @@ -596,9 +596,9 @@ static int act8945a_charger_probe(struct platform_device *pdev) return ret; irq = of_irq_get(pdev->dev.of_node, 0); - if (irq == -EPROBE_DEFER) { + if (irq <= 0) { dev_err(&pdev->dev, "failed to find IRQ number\n"); - return -EPROBE_DEFER; + return irq ?: -ENXIO; } ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed, diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index d5a707e14526..35ff406aca48 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -16,6 +16,9 @@ #include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/power_supply.h> +#include <linux/power/bq24190_charger.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> #include <linux/workqueue.h> #include <linux/gpio.h> #include <linux/i2c.h> @@ -43,6 +46,8 @@ #define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2 #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 +#define BQ24190_REG_POC_SYS_MIN_MIN 3000 +#define BQ24190_REG_POC_SYS_MIN_MAX 3700 #define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0) #define BQ24190_REG_POC_BOOST_LIM_SHIFT 0 @@ -57,9 +62,13 @@ #define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ BIT(4)) #define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4 +#define BQ24190_REG_PCTCC_IPRECHG_MIN 128 +#define BQ24190_REG_PCTCC_IPRECHG_MAX 2048 #define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \ BIT(0)) #define BQ24190_REG_PCTCC_ITERM_SHIFT 0 +#define BQ24190_REG_PCTCC_ITERM_MIN 128 +#define BQ24190_REG_PCTCC_ITERM_MAX 2048 #define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */ #define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \ @@ -156,9 +165,13 @@ struct bq24190_dev_info { struct extcon_dev *extcon; struct notifier_block extcon_nb; struct delayed_work extcon_work; + struct delayed_work input_current_limit_work; char model_name[I2C_NAME_SIZE]; bool initialized; bool irq_event; + u16 sys_min; + u16 iprechg; + u16 iterm; struct mutex f_reg_lock; u8 f_reg; u8 ss_reg; @@ -504,15 +517,112 @@ static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} #endif -/* - * According to the "Host Mode and default Mode" section of the - * manual, a write to any register causes the bq24190 to switch - * from default mode to host mode. It will switch back to default - * mode after a WDT timeout unless the WDT is turned off as well. - * So, by simply turning off the WDT, we accomplish both with the - * same write. - */ -static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) +#ifdef CONFIG_REGULATOR +static int bq24190_set_charge_mode(struct regulator_dev *dev, u8 val) +{ + struct bq24190_dev_info *bdi = rdev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + pm_runtime_put_noidle(bdi->dev); + return ret; + } + + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, val); + + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + return ret; +} + +static int bq24190_vbus_enable(struct regulator_dev *dev) +{ + return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_OTG); +} + +static int bq24190_vbus_disable(struct regulator_dev *dev) +{ + return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_CHARGE); +} + +static int bq24190_vbus_is_enabled(struct regulator_dev *dev) +{ + struct bq24190_dev_info *bdi = rdev_get_drvdata(dev); + int ret; + u8 val; + + ret = pm_runtime_get_sync(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + pm_runtime_put_noidle(bdi->dev); + return ret; + } + + ret = bq24190_read_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, &val); + + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + return ret ? ret : val == BQ24190_REG_POC_CHG_CONFIG_OTG; +} + +static const struct regulator_ops bq24190_vbus_ops = { + .enable = bq24190_vbus_enable, + .disable = bq24190_vbus_disable, + .is_enabled = bq24190_vbus_is_enabled, +}; + +static const struct regulator_desc bq24190_vbus_desc = { + .name = "usb_otg_vbus", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &bq24190_vbus_ops, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static const struct regulator_init_data bq24190_vbus_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi) +{ + struct bq24190_platform_data *pdata = bdi->dev->platform_data; + struct regulator_config cfg = { }; + struct regulator_dev *reg; + int ret = 0; + + cfg.dev = bdi->dev; + if (pdata && pdata->regulator_init_data) + cfg.init_data = pdata->regulator_init_data; + else + cfg.init_data = &bq24190_vbus_init_data; + cfg.driver_data = bdi; + reg = devm_regulator_register(bdi->dev, &bq24190_vbus_desc, &cfg); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(bdi->dev, "Can't register regulator: %d\n", ret); + } + + return ret; +} +#else +static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi) +{ + return 0; +} +#endif + +static int bq24190_set_config(struct bq24190_dev_info *bdi) { int ret; u8 v; @@ -523,9 +633,52 @@ static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> BQ24190_REG_CTTC_WATCHDOG_SHIFT); + + /* + * According to the "Host Mode and default Mode" section of the + * manual, a write to any register causes the bq24190 to switch + * from default mode to host mode. It will switch back to default + * mode after a WDT timeout unless the WDT is turned off as well. + * So, by simply turning off the WDT, we accomplish both with the + * same write. + */ v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; - return bq24190_write(bdi, BQ24190_REG_CTTC, v); + ret = bq24190_write(bdi, BQ24190_REG_CTTC, v); + if (ret < 0) + return ret; + + if (bdi->sys_min) { + v = bdi->sys_min / 100 - 30; // manual section 9.5.1.2, table 9 + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_SYS_MIN_MASK, + BQ24190_REG_POC_SYS_MIN_SHIFT, + v); + if (ret < 0) + return ret; + } + + if (bdi->iprechg) { + v = bdi->iprechg / 128 - 1; // manual section 9.5.1.4, table 11 + ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_IPRECHG_MASK, + BQ24190_REG_PCTCC_IPRECHG_SHIFT, + v); + if (ret < 0) + return ret; + } + + if (bdi->iterm) { + v = bdi->iterm / 128 - 1; // manual section 9.5.1.4, table 11 + ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_ITERM_MASK, + BQ24190_REG_PCTCC_ITERM_SHIFT, + v); + if (ret < 0) + return ret; + } + + return 0; } static int bq24190_register_reset(struct bq24190_dev_info *bdi) @@ -773,6 +926,38 @@ static int bq24190_charger_set_temp_alert_max(struct bq24190_dev_info *bdi, return bq24190_battery_set_temp_alert_max(bdi, val); } +static int bq24190_charger_get_precharge(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_IPRECHG_MASK, + BQ24190_REG_PCTCC_IPRECHG_SHIFT, &v); + if (ret < 0) + return ret; + + val->intval = ++v * 128 * 1000; + return 0; +} + +static int bq24190_charger_get_charge_term(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_ITERM_MASK, + BQ24190_REG_PCTCC_ITERM_SHIFT, &v); + if (ret < 0) + return ret; + + val->intval = ++v * 128 * 1000; + return 0; +} + static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, union power_supply_propval *val) { @@ -865,6 +1050,33 @@ static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); } +static int bq24190_charger_get_iinlimit(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int iinlimit, ret; + + ret = bq24190_get_field_val(bdi, BQ24190_REG_ISC, + BQ24190_REG_ISC_IINLIM_MASK, + BQ24190_REG_ISC_IINLIM_SHIFT, + bq24190_isc_iinlim_values, + ARRAY_SIZE(bq24190_isc_iinlim_values), &iinlimit); + if (ret < 0) + return ret; + + val->intval = iinlimit; + return 0; +} + +static int bq24190_charger_set_iinlimit(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_set_field_val(bdi, BQ24190_REG_ISC, + BQ24190_REG_ISC_IINLIM_MASK, + BQ24190_REG_ISC_IINLIM_SHIFT, + bq24190_isc_iinlim_values, + ARRAY_SIZE(bq24190_isc_iinlim_values), val->intval); +} + static int bq24190_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -893,6 +1105,12 @@ static int bq24190_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ret = bq24190_charger_get_temp_alert_max(bdi, val); break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq24190_charger_get_precharge(bdi, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = bq24190_charger_get_charge_term(bdi, val); + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq24190_charger_get_current(bdi, val); break; @@ -905,6 +1123,9 @@ static int bq24190_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ret = bq24190_charger_get_voltage_max(bdi, val); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq24190_charger_get_iinlimit(bdi, val); + break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_SYSTEM; ret = 0; @@ -956,6 +1177,9 @@ static int bq24190_charger_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = bq24190_charger_set_voltage(bdi, val); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq24190_charger_set_iinlimit(bdi, val); + break; default: ret = -EINVAL; } @@ -977,6 +1201,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_TYPE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = 1; break; default: @@ -986,16 +1211,45 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, return ret; } +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); + + power_supply_set_input_current_limit_from_supplier(bdi->charger); +} + +/* Sync the input-current-limit with our parent supply (if we have one) */ +static void bq24190_charger_external_power_changed(struct power_supply *psy) +{ + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); + + /* + * The Power-Good detection may take up to 220ms, sometimes + * the external charger detection is quicker, and the bq24190 will + * reset to iinlim based on its own charger detection (which is not + * hooked up when using external charger detection) resulting in a + * too low default 500mA iinlim. Delay setting the input-current-limit + * for 300ms to avoid this. + */ + queue_delayed_work(system_wq, &bdi->input_current_limit_work, + msecs_to_jiffies(300)); +} + static enum power_supply_property bq24190_charger_properties[] = { POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, @@ -1013,6 +1267,7 @@ static const struct power_supply_desc bq24190_charger_desc = { .get_property = bq24190_charger_get_property, .set_property = bq24190_charger_set_property, .property_is_writeable = bq24190_charger_property_is_writeable, + .external_power_changed = bq24190_charger_external_power_changed, }; /* Battery power supply property routines */ @@ -1460,13 +1715,50 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi) if (ret < 0) return ret; - ret = bq24190_set_mode_host(bdi); + ret = bq24190_set_config(bdi); if (ret < 0) return ret; return bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg); } +static int bq24190_get_config(struct bq24190_dev_info *bdi) +{ + const char * const s = "ti,system-minimum-microvolt"; + struct power_supply_battery_info info = {}; + int v; + + if (device_property_read_u32(bdi->dev, s, &v) == 0) { + v /= 1000; + if (v >= BQ24190_REG_POC_SYS_MIN_MIN + && v <= BQ24190_REG_POC_SYS_MIN_MAX) + bdi->sys_min = v; + else + dev_warn(bdi->dev, "invalid value for %s: %u\n", s, v); + } + + if (bdi->dev->of_node && + !power_supply_get_battery_info(bdi->charger, &info)) { + v = info.precharge_current_ua / 1000; + if (v >= BQ24190_REG_PCTCC_IPRECHG_MIN + && v <= BQ24190_REG_PCTCC_IPRECHG_MAX) + bdi->iprechg = v; + else + dev_warn(bdi->dev, "invalid value for battery:precharge-current-microamp: %d\n", + v); + + v = info.charge_term_current_ua / 1000; + if (v >= BQ24190_REG_PCTCC_ITERM_MIN + && v <= BQ24190_REG_PCTCC_ITERM_MAX) + bdi->iterm = v; + else + dev_warn(bdi->dev, "invalid value for battery:charge-term-current-microamp: %d\n", + v); + } + + return 0; +} + static int bq24190_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1494,10 +1786,12 @@ static int bq24190_probe(struct i2c_client *client, mutex_init(&bdi->f_reg_lock); bdi->f_reg = 0; bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */ + INIT_DELAYED_WORK(&bdi->input_current_limit_work, + bq24190_input_current_limit_work); i2c_set_clientdata(client, bdi); - if (!client->irq) { + if (client->irq <= 0) { dev_err(dev, "Can't get irq info\n"); return -EINVAL; } @@ -1530,13 +1824,8 @@ static int bq24190_probe(struct i2c_client *client, goto out_pmrt; } - ret = bq24190_hw_init(bdi); - if (ret < 0) { - dev_err(dev, "Hardware init failed\n"); - goto out_pmrt; - } - charger_cfg.drv_data = bdi; + charger_cfg.of_node = dev->of_node; charger_cfg.supplied_to = bq24190_charger_supplied_to; charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to), bdi->charger = power_supply_register(dev, &bq24190_charger_desc, @@ -1560,8 +1849,20 @@ static int bq24190_probe(struct i2c_client *client, } } + ret = bq24190_get_config(bdi); + if (ret < 0) { + dev_err(dev, "Can't get devicetree config\n"); + goto out_charger; + } + + ret = bq24190_hw_init(bdi); + if (ret < 0) { + dev_err(dev, "Hardware init failed\n"); + goto out_charger; + } + ret = bq24190_sysfs_create_group(bdi); - if (ret) { + if (ret < 0) { dev_err(dev, "Can't create sysfs entries\n"); goto out_charger; } @@ -1577,6 +1878,10 @@ static int bq24190_probe(struct i2c_client *client, goto out_sysfs; } + ret = bq24190_register_vbus_regulator(bdi); + if (ret < 0) + goto out_sysfs; + if (bdi->extcon) { INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work); bdi->extcon_nb.notifier_call = bq24190_extcon_event; @@ -1704,7 +2009,7 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev) } bq24190_register_reset(bdi); - bq24190_set_mode_host(bdi); + bq24190_set_config(bdi); bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg); if (error >= 0) { @@ -1736,6 +2041,7 @@ MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); #ifdef CONFIG_OF static const struct of_device_id bq24190_of_match[] = { { .compatible = "ti,bq24190", }, + { .compatible = "ti,bq24192i", }, { }, }; MODULE_DEVICE_TABLE(of, bq24190_of_match); diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index ed44439d0112..51f0961ecf3e 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -58,8 +58,6 @@ #include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" - #define BQ27XXX_MANUFACTURER "Texas Instruments" /* BQ27XXX Flags */ @@ -132,8 +130,8 @@ enum bq27xxx_reg_index { [BQ27XXX_DM_CKSUM] = 0x60 /* Register mappings */ -static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { - [BQ27000] = { +static u8 + bq27000_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -157,7 +155,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, }, - [BQ27010] = { + bq27010_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -181,7 +179,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, }, - [BQ2750X] = { + bq2750x_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -201,47 +199,9 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ2751X] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = 0x28, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, - [BQ27XXX_REG_TTES] = 0x1a, - [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x1e, - [BQ27XXX_REG_AE] = INVALID_REG_ADDR, - [BQ27XXX_REG_SOC] = 0x20, - [BQ27XXX_REG_DCAP] = 0x2e, - [BQ27XXX_REG_AP] = INVALID_REG_ADDR, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27500] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = 0x18, - [BQ27XXX_REG_TTES] = 0x1c, - [BQ27XXX_REG_TTECP] = 0x26, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, - [BQ27XXX_REG_SOC] = 0x2c, - [BQ27XXX_REG_DCAP] = 0x3c, - [BQ27XXX_REG_AP] = 0x24, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27510G1] = { +#define bq2751x_regs bq27510g3_regs +#define bq2752x_regs bq27510g3_regs + bq27500_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -261,27 +221,9 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27510G2] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = 0x18, - [BQ27XXX_REG_TTES] = 0x1c, - [BQ27XXX_REG_TTECP] = 0x26, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, - [BQ27XXX_REG_SOC] = 0x2c, - [BQ27XXX_REG_DCAP] = 0x3c, - [BQ27XXX_REG_AP] = 0x24, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27510G3] = { +#define bq27510g1_regs bq27500_regs +#define bq27510g2_regs bq27500_regs + bq27510g3_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -301,7 +243,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G1] = { + bq27520g1_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -321,7 +263,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G2] = { + bq27520g2_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x36, @@ -341,7 +283,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G3] = { + bq27520g3_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x36, @@ -361,7 +303,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G4] = { + bq27520g4_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -381,7 +323,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ27530] = { + bq27530_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x32, @@ -401,7 +343,8 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27541] = { +#define bq27531_regs bq27530_regs + bq27541_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -421,7 +364,10 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27545] = { +#define bq27542_regs bq27541_regs +#define bq27546_regs bq27541_regs +#define bq27742_regs bq27541_regs + bq27545_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -441,7 +387,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27421] = { + bq27421_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x02, [BQ27XXX_REG_INT_TEMP] = 0x1e, @@ -460,10 +406,12 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x18, BQ27XXX_DM_REG_ROWS, - }, -}; + }; +#define bq27425_regs bq27421_regs +#define bq27441_regs bq27421_regs +#define bq27621_regs bq27421_regs -static enum power_supply_property bq27000_battery_props[] = { +static enum power_supply_property bq27000_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -485,7 +433,7 @@ static enum power_supply_property bq27000_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27010_battery_props[] = { +static enum power_supply_property bq27010_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -505,25 +453,11 @@ static enum power_supply_property bq27010_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq2750x_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_MANUFACTURER, -}; +#define bq2750x_props bq27510g3_props +#define bq2751x_props bq27510g3_props +#define bq2752x_props bq27510g3_props -static enum power_supply_property bq2751x_battery_props[] = { +static enum power_supply_property bq27500_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -532,16 +466,21 @@ static enum power_supply_property bq2751x_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27510g1_props bq27500_props +#define bq27510g2_props bq27500_props -static enum power_supply_property bq27500_battery_props[] = { +static enum power_supply_property bq27510g3_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -550,19 +489,16 @@ static enum power_supply_property bq27500_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, - POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g1_battery_props[] = { +static enum power_supply_property bq27520g1_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -576,14 +512,15 @@ static enum power_supply_property bq27510g1_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g2_battery_props[] = { +#define bq27520g2_props bq27500_props + +static enum power_supply_property bq27520g3_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -592,7 +529,6 @@ static enum power_supply_property bq27510g2_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, @@ -604,7 +540,7 @@ static enum power_supply_property bq27510g2_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g3_battery_props[] = { +static enum power_supply_property bq27520g4_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -616,13 +552,12 @@ static enum power_supply_property bq27510g3_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27520g1_battery_props[] = { +static enum power_supply_property bq27530_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -631,18 +566,17 @@ static enum power_supply_property bq27520g1_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27531_props bq27530_props -static enum power_supply_property bq27520g2_battery_props[] = { +static enum power_supply_property bq27541_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -651,19 +585,20 @@ static enum power_supply_property bq27520g2_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27542_props bq27541_props +#define bq27546_props bq27541_props +#define bq27742_props bq27541_props -static enum power_supply_property bq27520g3_battery_props[] = { +static enum power_supply_property bq27545_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -675,15 +610,13 @@ static enum power_supply_property bq27520g3_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27520g4_battery_props[] = { +static enum power_supply_property bq27421_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -691,111 +624,144 @@ static enum power_supply_property bq27520g4_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27425_props bq27421_props +#define bq27441_props bq27421_props +#define bq27621_props bq27421_props -static enum power_supply_property bq27530_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_MANUFACTURER, +struct bq27xxx_dm_reg { + u8 subclass_id; + u8 offset; + u8 bytes; + u16 min, max; }; -static enum power_supply_property bq27541_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_MANUFACTURER, +enum bq27xxx_dm_reg_id { + BQ27XXX_DM_DESIGN_CAPACITY = 0, + BQ27XXX_DM_DESIGN_ENERGY, + BQ27XXX_DM_TERMINATE_VOLTAGE, }; -static enum power_supply_property bq27545_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_MANUFACTURER, +#define bq27000_dm_regs 0 +#define bq27010_dm_regs 0 +#define bq2750x_dm_regs 0 +#define bq2751x_dm_regs 0 +#define bq2752x_dm_regs 0 + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27500_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */ + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 }, }; +#else +#define bq27500_dm_regs 0 +#endif -static enum power_supply_property bq27421_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_MANUFACTURER, +/* todo create data memory definitions from datasheets and test on chips */ +#define bq27510g1_dm_regs 0 +#define bq27510g2_dm_regs 0 +#define bq27510g3_dm_regs 0 +#define bq27520g1_dm_regs 0 +#define bq27520g2_dm_regs 0 +#define bq27520g3_dm_regs 0 +#define bq27520g4_dm_regs 0 +#define bq27530_dm_regs 0 +#define bq27531_dm_regs 0 +#define bq27541_dm_regs 0 +#define bq27542_dm_regs 0 +#define bq27546_dm_regs 0 +#define bq27742_dm_regs 0 + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27545_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 }, }; +#else +#define bq27545_dm_regs 0 +#endif -#define BQ27XXX_PROP(_id, _prop) \ - [_id] = { \ - .props = _prop, \ - .size = ARRAY_SIZE(_prop), \ - } +static struct bq27xxx_dm_reg bq27421_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 }, +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 }, +}; + +#if 0 /* not yet tested */ +#define bq27441_dm_regs bq27421_dm_regs +#else +#define bq27441_dm_regs 0 +#endif + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 }, +}; +#else +#define bq27621_dm_regs 0 +#endif + +#define BQ27XXX_O_ZERO 0x00000001 +#define BQ27XXX_O_OTDC 0x00000002 +#define BQ27XXX_O_UTOT 0x00000004 +#define BQ27XXX_O_CFGUP 0x00000008 +#define BQ27XXX_O_RAM 0x00000010 + +#define BQ27XXX_DATA(ref, key, opt) { \ + .opts = (opt), \ + .unseal_key = key, \ + .regs = ref##_regs, \ + .dm_regs = ref##_dm_regs, \ + .props = ref##_props, \ + .props_size = ARRAY_SIZE(ref##_props) } static struct { + u32 opts; + u32 unseal_key; + u8 *regs; + struct bq27xxx_dm_reg *dm_regs; enum power_supply_property *props; - size_t size; -} bq27xxx_battery_props[] = { - BQ27XXX_PROP(BQ27000, bq27000_battery_props), - BQ27XXX_PROP(BQ27010, bq27010_battery_props), - BQ27XXX_PROP(BQ2750X, bq2750x_battery_props), - BQ27XXX_PROP(BQ2751X, bq2751x_battery_props), - BQ27XXX_PROP(BQ27500, bq27500_battery_props), - BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props), - BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props), - BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props), - BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props), - BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props), - BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props), - BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props), - BQ27XXX_PROP(BQ27530, bq27530_battery_props), - BQ27XXX_PROP(BQ27541, bq27541_battery_props), - BQ27XXX_PROP(BQ27545, bq27545_battery_props), - BQ27XXX_PROP(BQ27421, bq27421_battery_props), + size_t props_size; +} bq27xxx_chip_data[] = { + [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO), + [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO), + [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), + [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), + [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), + [BQ27500] = BQ27XXX_DATA(bq27500, 0x04143672, BQ27XXX_O_OTDC), + [BQ27510G1] = BQ27XXX_DATA(bq27510g1, 0 , BQ27XXX_O_OTDC), + [BQ27510G2] = BQ27XXX_DATA(bq27510g2, 0 , BQ27XXX_O_OTDC), + [BQ27510G3] = BQ27XXX_DATA(bq27510g3, 0 , BQ27XXX_O_OTDC), + [BQ27520G1] = BQ27XXX_DATA(bq27520g1, 0 , BQ27XXX_O_OTDC), + [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), + [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), + [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), + [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), + [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), + [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), + [BQ27542] = BQ27XXX_DATA(bq27542, 0 , BQ27XXX_O_OTDC), + [BQ27546] = BQ27XXX_DATA(bq27546, 0 , BQ27XXX_O_OTDC), + [BQ27742] = BQ27XXX_DATA(bq27742, 0 , BQ27XXX_O_OTDC), + [BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC), + [BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), + [BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP), + [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), + [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), }; static DEFINE_MUTEX(bq27xxx_list_lock); @@ -805,13 +771,6 @@ static LIST_HEAD(bq27xxx_battery_devices); #define BQ27XXX_DM_SZ 32 -struct bq27xxx_dm_reg { - u8 subclass_id; - u8 offset; - u8 bytes; - u16 min, max; -}; - /** * struct bq27xxx_dm_buf - chip data memory buffer * @class: data memory subclass_id @@ -844,12 +803,6 @@ static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf, return NULL; } -enum bq27xxx_dm_reg_id { - BQ27XXX_DM_DESIGN_CAPACITY = 0, - BQ27XXX_DM_DESIGN_ENERGY, - BQ27XXX_DM_TERMINATE_VOLTAGE, -}; - static const char * const bq27xxx_dm_reg_name[] = { [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity", [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy", @@ -1092,9 +1045,9 @@ static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di, } #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM - if (!di->ram_chip && !bq27xxx_dt_to_nvm) { + if (!(di->opts & BQ27XXX_O_RAM) && !bq27xxx_dt_to_nvm) { #else - if (!di->ram_chip) { + if (!(di->opts & BQ27XXX_O_RAM)) { #endif /* devicetree and NVM differ; defer to NVM */ dev_warn(di->dev, "%s has %u; update to %u disallowed " @@ -1130,7 +1083,7 @@ static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool a return ret; } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try); - if (!try) { + if (!try && di->chip != BQ27425) { // 425 has a bug dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active); return -EINVAL; } @@ -1162,7 +1115,7 @@ static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di) static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di, struct bq27xxx_dm_buf *buf) { - bool cfgup = di->chip == BQ27421; /* assume related chips need cfgupdate */ + bool cfgup = di->opts & BQ27XXX_O_CFGUP; int ret; if (!buf->dirty) @@ -1261,7 +1214,7 @@ static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di, bq27xxx_battery_seal(di); - if (updated && di->chip != BQ27421) { /* not a cfgupdate chip, so reset */ + if (updated && !(di->opts & BQ27XXX_O_CFGUP)) { bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false); BQ27XXX_MSLEEP(300); /* reset time is not documented */ } @@ -1328,7 +1281,7 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) { int soc; - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); else soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); @@ -1354,7 +1307,7 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg) return charge; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; else charge *= 1000; @@ -1370,7 +1323,7 @@ static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di) { int flags; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); if (flags >= 0 && (flags & BQ27000_FLAG_CI)) return -ENODATA; @@ -1396,7 +1349,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) { int dcap; - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true); else dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); @@ -1406,7 +1359,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) return dcap; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; else dcap *= 1000; @@ -1428,7 +1381,7 @@ static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di) return ae; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS; else ae *= 1000; @@ -1450,7 +1403,7 @@ static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di) return temp; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) temp = 5 * temp / 2; return temp; @@ -1507,7 +1460,7 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) return tval; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS; else return tval; @@ -1518,26 +1471,12 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) */ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) { - switch (di->chip) { - case BQ2750X: - case BQ2751X: - case BQ27500: - case BQ27510G1: - case BQ27510G2: - case BQ27510G3: - case BQ27520G1: - case BQ27520G2: - case BQ27520G3: - case BQ27520G4: - case BQ27541: - case BQ27545: + if (di->opts & BQ27XXX_O_OTDC) return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); - case BQ27530: - case BQ27421: + if (di->opts & BQ27XXX_O_UTOT) return flags & BQ27XXX_FLAG_OT; - default: - return false; - } + + return false; } /* @@ -1545,7 +1484,7 @@ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) */ static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) { - if (di->chip == BQ27530 || di->chip == BQ27421) + if (di->opts & BQ27XXX_O_UTOT) return flags & BQ27XXX_FLAG_UT; return false; @@ -1556,7 +1495,7 @@ static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) */ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) { - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); else return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); @@ -1569,7 +1508,7 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) { int flags; - bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; + bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); if (flags < 0) { @@ -1591,8 +1530,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) void bq27xxx_battery_update(struct bq27xxx_device_info *di) { struct bq27xxx_reg_cache cache = {0, }; - bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010; - bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; + bool has_ci_flag = di->opts & BQ27XXX_O_ZERO; + bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); if ((cache.flags & 0xff) == 0xff) @@ -1670,7 +1609,7 @@ static int bq27xxx_battery_current(struct bq27xxx_device_info *di, return curr; } - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); if (flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); @@ -1691,7 +1630,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, { int status; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) @@ -1719,7 +1658,7 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, { int level; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { if (di->cache.flags & BQ27000_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27000_FLAG_EDV1) @@ -1884,7 +1823,11 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); mutex_init(&di->lock); - di->regs = bq27xxx_regs[di->chip]; + + di->regs = bq27xxx_chip_data[di->chip].regs; + di->unseal_key = bq27xxx_chip_data[di->chip].unseal_key; + di->dm_regs = bq27xxx_chip_data[di->chip].dm_regs; + di->opts = bq27xxx_chip_data[di->chip].opts; psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); if (!psy_desc) @@ -1892,8 +1835,8 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) psy_desc->name = di->name; psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; - psy_desc->properties = bq27xxx_battery_props[di->chip].props; - psy_desc->num_properties = bq27xxx_battery_props[di->chip].size; + psy_desc->properties = bq27xxx_chip_data[di->chip].props; + psy_desc->num_properties = bq27xxx_chip_data[di->chip].props_size; psy_desc->get_property = bq27xxx_battery_get_property; psy_desc->external_power_changed = bq27xxx_external_power_changed; @@ -1903,8 +1846,6 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) return PTR_ERR(di->bat); } - dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); - bq27xxx_battery_settings(di); bq27xxx_battery_update(di); @@ -1938,110 +1879,6 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) } EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); -static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg, - bool single) -{ - struct device *dev = di->dev; - struct bq27xxx_platform_data *pdata = dev->platform_data; - unsigned int timeout = 3; - int upper, lower; - int temp; - - if (!single) { - /* Make sure the value has not changed in between reading the - * lower and the upper part */ - upper = pdata->read(dev, reg + 1); - do { - temp = upper; - if (upper < 0) - return upper; - - lower = pdata->read(dev, reg); - if (lower < 0) - return lower; - - upper = pdata->read(dev, reg + 1); - } while (temp != upper && --timeout); - - if (timeout == 0) - return -EIO; - - return (upper << 8) | lower; - } - - return pdata->read(dev, reg); -} - -static int bq27xxx_battery_platform_probe(struct platform_device *pdev) -{ - struct bq27xxx_device_info *di; - struct bq27xxx_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "no platform_data supplied\n"); - return -EINVAL; - } - - if (!pdata->read) { - dev_err(&pdev->dev, "no hdq read callback supplied\n"); - return -EINVAL; - } - - if (!pdata->chip) { - dev_err(&pdev->dev, "no device supplied\n"); - return -EINVAL; - } - - di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); - if (!di) - return -ENOMEM; - - platform_set_drvdata(pdev, di); - - di->dev = &pdev->dev; - di->chip = pdata->chip; - di->name = pdata->name ?: dev_name(&pdev->dev); - di->bus.read = bq27xxx_battery_platform_read; - - return bq27xxx_battery_setup(di); -} - -static int bq27xxx_battery_platform_remove(struct platform_device *pdev) -{ - struct bq27xxx_device_info *di = platform_get_drvdata(pdev); - - bq27xxx_battery_teardown(di); - - return 0; -} - -static const struct platform_device_id bq27xxx_battery_platform_id_table[] = { - { "bq27000-battery", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table); - -#ifdef CONFIG_OF -static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = { - { .compatible = "ti,bq27000" }, - {}, -}; -MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table); -#endif - -static struct platform_driver bq27xxx_battery_platform_driver = { - .probe = bq27xxx_battery_platform_probe, - .remove = bq27xxx_battery_platform_remove, - .driver = { - .name = "bq27000-battery", - .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table), - }, - .id_table = bq27xxx_battery_platform_id_table, -}; -module_platform_driver(bq27xxx_battery_platform_driver); - -MODULE_ALIAS("platform:bq27000-battery"); - MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/bq27xxx_battery_hdq.c b/drivers/power/supply/bq27xxx_battery_hdq.c new file mode 100644 index 000000000000..9aff896c9802 --- /dev/null +++ b/drivers/power/supply/bq27xxx_battery_hdq.c @@ -0,0 +1,135 @@ +/* + * BQ27xxx battery monitor HDQ/1-wire driver + * + * Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/power/bq27xxx_battery.h> + +#include <linux/w1.h> + +#define W1_FAMILY_BQ27000 0x01 + +#define HDQ_CMD_READ (0 << 7) +#define HDQ_CMD_WRITE (1 << 7) + +static int F_ID; +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device"); + +static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg) +{ + u8 val; + + mutex_lock(&sl->master->bus_mutex); + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + mutex_unlock(&sl->master->bus_mutex); + + return val; +} + +static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg, + bool single) +{ + struct w1_slave *sl = dev_to_w1_slave(di->dev); + unsigned int timeout = 3; + int upper, lower; + int temp; + + if (!single) { + /* + * Make sure the value has not changed in between reading the + * lower and the upper part + */ + upper = w1_bq27000_read(sl, reg + 1); + do { + temp = upper; + if (upper < 0) + return upper; + + lower = w1_bq27000_read(sl, reg); + if (lower < 0) + return lower; + + upper = w1_bq27000_read(sl, reg + 1); + } while (temp != upper && --timeout); + + if (timeout == 0) + return -EIO; + + return (upper << 8) | lower; + } + + return w1_bq27000_read(sl, reg); +} + +static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl) +{ + struct bq27xxx_device_info *di; + + di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + dev_set_drvdata(&sl->dev, di); + + di->dev = &sl->dev; + di->chip = BQ27000; + di->name = "bq27000-battery"; + di->bus.read = bq27xxx_battery_hdq_read; + + return bq27xxx_battery_setup(di); +} + +static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl) +{ + struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev); + + bq27xxx_battery_teardown(di); +} + +static struct w1_family_ops bq27xxx_battery_hdq_fops = { + .add_slave = bq27xxx_battery_hdq_add_slave, + .remove_slave = bq27xxx_battery_hdq_remove_slave, +}; + +static struct w1_family bq27xxx_battery_hdq_family = { + .fid = W1_FAMILY_BQ27000, + .fops = &bq27xxx_battery_hdq_fops, +}; + +static int __init bq27xxx_battery_hdq_init(void) +{ + if (F_ID) + bq27xxx_battery_hdq_family.fid = F_ID; + + return w1_register_family(&bq27xxx_battery_hdq_family); +} +module_init(bq27xxx_battery_hdq_init); + +static void __exit bq27xxx_battery_hdq_exit(void) +{ + w1_unregister_family(&bq27xxx_battery_hdq_family); +} +module_exit(bq27xxx_battery_hdq_exit); + +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000)); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index a5972214f074..0b11ed472f33 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -230,7 +230,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27210", BQ27010 }, { "bq27500", BQ2750X }, { "bq27510", BQ2751X }, - { "bq27520", BQ2751X }, + { "bq27520", BQ2752X }, { "bq27500-1", BQ27500 }, { "bq27510g1", BQ27510G1 }, { "bq27510g2", BQ27510G2 }, @@ -240,16 +240,16 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27520g3", BQ27520G3 }, { "bq27520g4", BQ27520G4 }, { "bq27530", BQ27530 }, - { "bq27531", BQ27530 }, + { "bq27531", BQ27531 }, { "bq27541", BQ27541 }, - { "bq27542", BQ27541 }, - { "bq27546", BQ27541 }, - { "bq27742", BQ27541 }, + { "bq27542", BQ27542 }, + { "bq27546", BQ27546 }, + { "bq27742", BQ27742 }, { "bq27545", BQ27545 }, { "bq27421", BQ27421 }, - { "bq27425", BQ27421 }, - { "bq27441", BQ27421 }, - { "bq27621", BQ27421 }, + { "bq27425", BQ27425 }, + { "bq27441", BQ27441 }, + { "bq27621", BQ27621 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index adc3761831e1..6502fa7c2106 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1632,8 +1632,7 @@ static int charger_manager_probe(struct platform_device *pdev) return -ENODEV; } - cm = devm_kzalloc(&pdev->dev, - sizeof(struct charger_manager), GFP_KERNEL); + cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); if (!cm) return -ENOMEM; @@ -1645,12 +1644,14 @@ static int charger_manager_probe(struct platform_device *pdev) /* Initialize alarm timer */ if (alarmtimer_get_rtcdev()) { cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); + if (!cm_timer) + return -ENOMEM; alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); } /* - * The following two do not need to be errors. - * Users may intentionally ignore those two features. + * Some of the following do not need to be errors. + * Users may intentionally ignore those features. */ if (desc->fullbatt_uV == 0) { dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index 8edd4aa5f475..e5d81b493c45 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -663,7 +663,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_param_eeprom_bin_attr = { +static const struct bin_attribute ds2780_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, @@ -708,7 +708,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_user_eeprom_bin_attr = { +static const struct bin_attribute ds2780_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index 4400402f9ec5..efe83ef8670c 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -665,7 +665,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_param_eeprom_bin_attr = { +static const struct bin_attribute ds2781_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, @@ -711,7 +711,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_user_eeprom_bin_attr = { +static const struct bin_attribute ds2781_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 677f7c40b25a..0f3432795f3c 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -626,7 +626,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev, { struct lp8788_charger *pchg = dev_get_drvdata(dev); enum lp8788_charging_state state; - char *desc[LP8788_MAX_CHG_STATE] = { + static const char * const desc[LP8788_MAX_CHG_STATE] = { [LP8788_OFF] = "CHARGER OFF", [LP8788_WARM_UP] = "WARM UP", [LP8788_LOW_INPUT] = "LOW INPUT STATE", @@ -650,8 +650,10 @@ static ssize_t lp8788_show_eoc_time(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); - char *stime[] = { "400ms", "5min", "10min", "15min", - "20min", "25min", "30min", "No timeout" }; + static const char * const stime[] = { + "400ms", "5min", "10min", "15min", + "20min", "25min", "30min", "No timeout" + }; u8 val; lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); @@ -665,9 +667,13 @@ static ssize_t lp8788_show_eoc_level(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); - char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" }; - char *relative_level[] = { "5%", "10%", "15%", "20%" }; - char *level; + static const char * const abs_level[] = { + "25mA", "49mA", "75mA", "98mA" + }; + static const char * const relative_level[] = { + "5%", "10%", "15%", "20%" + }; + const char *level; u8 val; u8 mode; diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 7efb908f4451..08e4fd9ee607 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -1,6 +1,6 @@ /* - * I2C client/driver for the Linear Technology LTC2941 and LTC2943 - * Battery Gas Gauge IC + * I2C client/driver for the Linear Technology LTC2941, LTC2942, LTC2943 + * and LTC2944 Battery Gas Gauge IC * * Copyright (C) 2014 Topic Embedded Systems * @@ -34,35 +34,39 @@ enum ltc294x_reg { LTC294X_REG_CONTROL = 0x01, LTC294X_REG_ACC_CHARGE_MSB = 0x02, LTC294X_REG_ACC_CHARGE_LSB = 0x03, - LTC294X_REG_THRESH_HIGH_MSB = 0x04, - LTC294X_REG_THRESH_HIGH_LSB = 0x05, - LTC294X_REG_THRESH_LOW_MSB = 0x06, - LTC294X_REG_THRESH_LOW_LSB = 0x07, - LTC294X_REG_VOLTAGE_MSB = 0x08, - LTC294X_REG_VOLTAGE_LSB = 0x09, - LTC294X_REG_CURRENT_MSB = 0x0E, - LTC294X_REG_CURRENT_LSB = 0x0F, - LTC294X_REG_TEMPERATURE_MSB = 0x14, - LTC294X_REG_TEMPERATURE_LSB = 0x15, + LTC294X_REG_VOLTAGE_MSB = 0x08, + LTC294X_REG_VOLTAGE_LSB = 0x09, + LTC2942_REG_TEMPERATURE_MSB = 0x0C, + LTC2942_REG_TEMPERATURE_LSB = 0x0D, + LTC2943_REG_CURRENT_MSB = 0x0E, + LTC2943_REG_CURRENT_LSB = 0x0F, + LTC2943_REG_TEMPERATURE_MSB = 0x14, + LTC2943_REG_TEMPERATURE_LSB = 0x15, }; -#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6)) -#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) +enum ltc294x_id { + LTC2941_ID, + LTC2942_ID, + LTC2943_ID, + LTC2944_ID, +}; + +#define LTC2941_REG_STATUS_CHIP_ID BIT(7) + +#define LTC2942_REG_CONTROL_MODE_SCAN (BIT(7) | BIT(6)) +#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) #define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3)) #define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0)) #define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) #define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 -#define LTC2941_NUM_REGS 0x08 -#define LTC2943_NUM_REGS 0x18 - struct ltc294x_info { struct i2c_client *client; /* I2C Client pointer */ struct power_supply *supply; /* Supply pointer */ struct power_supply_desc supply_desc; /* Supply description */ struct delayed_work work; /* Work scheduler */ - unsigned long num_regs; /* Number of registers (chip type) */ + enum ltc294x_id id; /* Chip type */ int charge; /* Last charge register content */ int r_sense; /* mOhm */ int Qlsb; /* nAh */ @@ -145,9 +149,18 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; - /* Put the 2943 into "monitor" mode, so it measures every 10 sec */ - if (info->num_regs == LTC2943_NUM_REGS) + /* Put device into "monitor" mode */ + switch (info->id) { + case LTC2942_ID: /* 2942 measures every 2 sec */ + control |= LTC2942_REG_CONTROL_MODE_SCAN; + break; + case LTC2943_ID: + case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */ control |= LTC2943_REG_CONTROL_MODE_SCAN; + break; + default: + break; + } if (value != control) { ret = ltc294x_write_regs(info->client, @@ -252,7 +265,24 @@ static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) ret = ltc294x_read_regs(info->client, LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; - *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */ + switch (info->id) { + case LTC2943_ID: + value *= 23600 * 2; + value /= 0xFFFF; + value *= 1000 / 2; + break; + case LTC2944_ID: + value *= 70800 / 5*4; + value /= 0xFFFF; + value *= 1000 * 5/4; + break; + default: + value *= 6000 * 10; + value /= 0xFFFF; + value *= 1000 / 10; + break; + } + *val = value; return ret; } @@ -263,27 +293,38 @@ static int ltc294x_get_current(const struct ltc294x_info *info, int *val) s32 value; ret = ltc294x_read_regs(info->client, - LTC294X_REG_CURRENT_MSB, &datar[0], 2); + LTC2943_REG_CURRENT_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; value -= 0x7FFF; + if (info->id == LTC2944_ID) + value *= 64000; + else + value *= 60000; /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, * the formula below keeps everything in s32 range while preserving * enough digits */ - *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ + *val = 1000 * (value / (info->r_sense * 0x7FFF)); /* in uA */ return ret; } static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) { + enum ltc294x_reg reg; int ret; u8 datar[2]; u32 value; - ret = ltc294x_read_regs(info->client, - LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2); - value = (datar[0] << 8) | datar[1]; - /* Full-scale is 510 Kelvin, convert to centidegrees */ - *val = (((51000 * value) / 0xFFFF) - 27215); + if (info->id == LTC2942_ID) { + reg = LTC2942_REG_TEMPERATURE_MSB; + value = 60000; /* Full-scale is 600 Kelvin */ + } else { + reg = LTC2943_REG_TEMPERATURE_MSB; + value = 51000; /* Full-scale is 510 Kelvin */ + } + ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); + value *= (datar[0] << 8) | datar[1]; + /* Convert to centidegrees */ + *val = value / 0xFFFF - 27215; return ret; } @@ -357,8 +398,8 @@ static enum power_supply_property ltc294x_properties[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, }; static int ltc294x_i2c_remove(struct i2c_client *client) @@ -375,10 +416,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client, { struct power_supply_config psy_cfg = {}; struct ltc294x_info *info; + struct device_node *np; int ret; u32 prescaler_exp; s32 r_sense; - struct device_node *np; + u8 status; info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) @@ -388,7 +430,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client, np = of_node_get(client->dev.of_node); - info->num_regs = (unsigned long)of_device_get_match_data(&client->dev); + info->id = (enum ltc294x_id)of_device_get_match_data(&client->dev); info->supply_desc.name = np->name; /* r_sense can be negative, when sense+ is connected to the battery @@ -409,7 +451,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client, prescaler_exp = LTC2941_MAX_PRESCALER_EXP; } - if (info->num_regs == LTC2943_NUM_REGS) { + if (info->id == LTC2943_ID) { if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP) prescaler_exp = LTC2943_MAX_PRESCALER_EXP; info->Qlsb = ((340 * 50000) / r_sense) / @@ -421,21 +463,39 @@ static int ltc294x_i2c_probe(struct i2c_client *client, (128 / (1 << prescaler_exp)); } + /* Read status register to check for LTC2942 */ + if (info->id == LTC2941_ID || info->id == LTC2942_ID) { + ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1); + if (ret < 0) { + dev_err(&client->dev, + "Could not read status register\n"); + return ret; + } + if (status & LTC2941_REG_STATUS_CHIP_ID) + info->id = LTC2941_ID; + else + info->id = LTC2942_ID; + } + info->client = client; info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY; info->supply_desc.properties = ltc294x_properties; - if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) + switch (info->id) { + case LTC2944_ID: + case LTC2943_ID: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties); - else if (info->num_regs >= LTC294X_REG_CURRENT_LSB) + break; + case LTC2942_ID: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 1; - else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB) - info->supply_desc.num_properties = - ARRAY_SIZE(ltc294x_properties) - 2; - else + break; + case LTC2941_ID: + default: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 3; + break; + } info->supply_desc.get_property = ltc294x_get_property; info->supply_desc.set_property = ltc294x_set_property; info->supply_desc.property_is_writeable = ltc294x_property_is_writeable; @@ -492,8 +552,10 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume); static const struct i2c_device_id ltc294x_i2c_id[] = { - {"ltc2941", LTC2941_NUM_REGS}, - {"ltc2943", LTC2943_NUM_REGS}, + { "ltc2941", LTC2941_ID, }, + { "ltc2942", LTC2942_ID, }, + { "ltc2943", LTC2943_ID, }, + { "ltc2944", LTC2944_ID, }, { }, }; MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); @@ -501,11 +563,19 @@ MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); static const struct of_device_id ltc294x_i2c_of_match[] = { { .compatible = "lltc,ltc2941", - .data = (void *)LTC2941_NUM_REGS + .data = (void *)LTC2941_ID, + }, + { + .compatible = "lltc,ltc2942", + .data = (void *)LTC2942_ID, }, { .compatible = "lltc,ltc2943", - .data = (void *)LTC2943_NUM_REGS + .data = (void *)LTC2943_ID, + }, + { + .compatible = "lltc,ltc2944", + .data = (void *)LTC2944_ID, }, { }, }; @@ -525,5 +595,5 @@ module_i2c_driver(ltc294x_driver); MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems"); MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products"); -MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver"); +MODULE_DESCRIPTION("LTC2941/LTC2942/LTC2943/LTC2944 Battery Gas Gauge IC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index aecaaa2b0586..5b556a13f517 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -22,6 +22,7 @@ * This driver is based on max17040_battery.c */ +#include <linux/acpi.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -982,6 +983,8 @@ static int max17042_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); const struct power_supply_desc *max17042_desc = &max17042_psy_desc; struct power_supply_config psy_cfg = {}; + const struct acpi_device_id *acpi_id = NULL; + struct device *dev = &client->dev; struct max17042_chip *chip; int ret; int i; @@ -995,7 +998,15 @@ static int max17042_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; - chip->chip_type = id->driver_data; + if (id) { + chip->chip_type = id->driver_data; + } else { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id) + return -ENODEV; + + chip->chip_type = acpi_id->driver_data; + } chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); if (IS_ERR(chip->regmap)) { dev_err(&client->dev, "Failed to initialize regmap\n"); @@ -1039,11 +1050,18 @@ static int max17042_probe(struct i2c_client *client, } if (client->irq) { + unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + /* + * On ACPI systems the IRQ may be handled by ACPI-event code, + * so we need to share (if the ACPI code is willing to share). + */ + if (acpi_id) + flags |= IRQF_SHARED | IRQF_PROBE_SHARED; + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, - max17042_thread_handler, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, + max17042_thread_handler, flags, chip->battery->desc->name, chip); if (!ret) { @@ -1053,10 +1071,13 @@ static int max17042_probe(struct i2c_client *client, max17042_set_soc_threshold(chip, 1); } else { client->irq = 0; - dev_err(&client->dev, "%s(): cannot get IRQ\n", - __func__); + if (ret != -EBUSY) + dev_err(&client->dev, "Failed to get IRQ\n"); } } + /* Not able to update the charge threshold when exceeded? -> disable */ + if (!client->irq) + regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00); regmap_read(chip->regmap, MAX17042_STATUS, &val); if (val & STATUS_POR_BIT) { @@ -1104,6 +1125,14 @@ static int max17042_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, max17042_resume); +#ifdef CONFIG_ACPI +static const struct acpi_device_id max17042_acpi_match[] = { + { "MAX17047", MAXIM_DEVICE_TYPE_MAX17047 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, max17042_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, @@ -1125,6 +1154,7 @@ MODULE_DEVICE_TABLE(i2c, max17042_id); static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", + .acpi_match_table = ACPI_PTR(max17042_acpi_match), .of_match_table = of_match_ptr(max17042_dt_match), .pm = &max17042_pm_ops, }, diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c new file mode 100644 index 000000000000..9ee601a03d9b --- /dev/null +++ b/drivers/power/supply/max1721x_battery.c @@ -0,0 +1,448 @@ +/* + * 1-Wire implementation for Maxim Semiconductor + * MAX7211/MAX17215 stanalone fuel gauge chip + * + * Copyright (C) 2017 Radioavionica Corporation + * Author: Alex A. Mihaylov <minimumlaw@rambler.ru> + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/w1.h> +#include <linux/regmap.h> +#include <linux/power_supply.h> + +#define W1_MAX1721X_FAMILY_ID 0x26 +#define DEF_DEV_NAME_MAX17211 "MAX17211" +#define DEF_DEV_NAME_MAX17215 "MAX17215" +#define DEF_DEV_NAME_UNKNOWN "UNKNOWN" +#define DEF_MFG_NAME "MAXIM" + +#define PSY_MAX_NAME_LEN 32 + +/* Number of valid register addresses in W1 mode */ +#define MAX1721X_MAX_REG_NR 0x1EF + +/* Factory settings (nonvilatile registers) (W1 specific) */ +#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */ +/* Strings */ +#define MAX1721X_REG_MFG_STR 0x1CC +#define MAX1721X_REG_MFG_NUMB 3 +#define MAX1721X_REG_DEV_STR 0x1DB +#define MAX1721X_REG_DEV_NUMB 5 +/* HEX Strings */ +#define MAX1721X_REG_SER_HEX 0x1D8 + +/* MAX172XX Output Registers for W1 chips */ +#define MAX172XX_REG_STATUS 0x000 /* status reg */ +#define MAX172XX_BAT_PRESENT (1<<4) /* battery connected bit */ +#define MAX172XX_REG_DEVNAME 0x021 /* chip config */ +#define MAX172XX_DEV_MASK 0x000F /* chip type mask */ +#define MAX172X1_DEV 0x0001 +#define MAX172X5_DEV 0x0005 +#define MAX172XX_REG_TEMP 0x008 /* Temperature */ +#define MAX172XX_REG_BATT 0x0DA /* Battery voltage */ +#define MAX172XX_REG_CURRENT 0x00A /* Actual current */ +#define MAX172XX_REG_AVGCURRENT 0x00B /* Average current */ +#define MAX172XX_REG_REPSOC 0x006 /* Percentage of charge */ +#define MAX172XX_REG_DESIGNCAP 0x018 /* Design capacity */ +#define MAX172XX_REG_REPCAP 0x005 /* Average capacity */ +#define MAX172XX_REG_TTE 0x011 /* Time to empty */ +#define MAX172XX_REG_TTF 0x020 /* Time to full */ + +struct max17211_device_info { + char name[PSY_MAX_NAME_LEN]; + struct power_supply *bat; + struct power_supply_desc bat_desc; + struct device *w1_dev; + struct regmap *regmap; + /* battery design format */ + unsigned int rsense; /* in tenths uOhm */ + char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1]; + char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1]; + char SerialNumber[13]; /* see get_sn_str() later for comment */ +}; + +/* Convert regs value to power_supply units */ + +static inline int max172xx_time_to_ps(unsigned int reg) +{ + return reg * 5625 / 1000; /* in sec. */ +} + +static inline int max172xx_percent_to_ps(unsigned int reg) +{ + return reg / 256; /* in percent from 0 to 100 */ +} + +static inline int max172xx_voltage_to_ps(unsigned int reg) +{ + return reg * 1250; /* in uV */ +} + +static inline int max172xx_capacity_to_ps(unsigned int reg) +{ + return reg * 500; /* in uAh */ +} + +/* + * Current and temperature is signed values, so unsigned regs + * value must be converted to signed type + */ + +static inline int max172xx_temperature_to_ps(unsigned int reg) +{ + int val = (int16_t)(reg); + + return val * 10 / 256; /* in tenths of deg. C */ +} + +/* + * Calculating current registers resolution: + * + * RSense stored in 10^-5 Ohm, so mesaurment voltage must be + * in 10^-11 Volts for get current in uA. + * 16 bit current reg fullscale +/-51.2mV is 102400 uV. + * So: 102400 / 65535 * 10^5 = 156252 + */ +static inline int max172xx_current_to_voltage(unsigned int reg) +{ + int val = (int16_t)(reg); + + return val * 156252; +} + + +static inline struct max17211_device_info * +to_device_info(struct power_supply *psy) +{ + return power_supply_get_drvdata(psy); +} + +static int max1721x_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max17211_device_info *info = to_device_info(psy); + unsigned int reg = 0; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + /* + * POWER_SUPPLY_PROP_PRESENT will always readable via + * sysfs interface. Value return 0 if battery not + * present or unaccesable via W1. + */ + val->intval = + regmap_read(info->regmap, MAX172XX_REG_STATUS, + ®) ? 0 : !(reg & MAX172XX_BAT_PRESENT); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, ®); + val->intval = max172xx_percent_to_ps(reg); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = regmap_read(info->regmap, MAX172XX_REG_BATT, ®); + val->intval = max172xx_voltage_to_ps(reg); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, ®); + val->intval = max172xx_capacity_to_ps(reg); + break; + case POWER_SUPPLY_PROP_CHARGE_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, ®); + val->intval = max172xx_capacity_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_TTE, ®); + val->intval = max172xx_time_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_TTF, ®); + val->intval = max172xx_time_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, ®); + val->intval = max172xx_temperature_to_ps(reg); + break; + /* We need signed current, so must cast info->rsense to signed type */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, ®); + val->intval = + max172xx_current_to_voltage(reg) / (int)info->rsense; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, ®); + val->intval = + max172xx_current_to_voltage(reg) / (int)info->rsense; + break; + /* + * Strings already received and inited by probe. + * We do dummy read for check battery still available. + */ + case POWER_SUPPLY_PROP_MODEL_NAME: + ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, ®); + val->strval = info->DeviceName; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, ®); + val->strval = info->ManufacturerName; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, ®); + val->strval = info->SerialNumber; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property max1721x_battery_props[] = { + /* int */ + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + /* strings */ + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +static int get_string(struct max17211_device_info *info, + uint16_t reg, uint8_t nr, char *str) +{ + unsigned int val; + + if (!str || !(reg == MAX1721X_REG_MFG_STR || + reg == MAX1721X_REG_DEV_STR)) + return -EFAULT; + + while (nr--) { + if (regmap_read(info->regmap, reg++, &val)) + return -EFAULT; + *str++ = val>>8 & 0x00FF; + *str++ = val & 0x00FF; + } + return 0; +} + +/* Maxim say: Serial number is a hex string up to 12 hex characters */ +static int get_sn_string(struct max17211_device_info *info, char *str) +{ + unsigned int val[3]; + + if (!str) + return -EFAULT; + + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0])) + return -EFAULT; + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1])) + return -EFAULT; + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2])) + return -EFAULT; + + snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]); + return 0; +} + +/* + * MAX1721x registers description for w1-regmap + */ +static const struct regmap_range max1721x_allow_range[] = { + regmap_reg_range(0, 0xDF), /* volatile data */ + regmap_reg_range(0x180, 0x1DF), /* non-volatile memory */ + regmap_reg_range(0x1E0, 0x1EF), /* non-volatile history (unused) */ +}; + +static const struct regmap_range max1721x_deny_range[] = { + /* volatile data unused registers */ + regmap_reg_range(0x24, 0x26), + regmap_reg_range(0x30, 0x31), + regmap_reg_range(0x33, 0x34), + regmap_reg_range(0x37, 0x37), + regmap_reg_range(0x3B, 0x3C), + regmap_reg_range(0x40, 0x41), + regmap_reg_range(0x43, 0x44), + regmap_reg_range(0x47, 0x49), + regmap_reg_range(0x4B, 0x4C), + regmap_reg_range(0x4E, 0xAF), + regmap_reg_range(0xB1, 0xB3), + regmap_reg_range(0xB5, 0xB7), + regmap_reg_range(0xBF, 0xD0), + regmap_reg_range(0xDB, 0xDB), + /* hole between volatile and non-volatile registers */ + regmap_reg_range(0xE0, 0x17F), +}; + +static const struct regmap_access_table max1721x_regs = { + .yes_ranges = max1721x_allow_range, + .n_yes_ranges = ARRAY_SIZE(max1721x_allow_range), + .no_ranges = max1721x_deny_range, + .n_no_ranges = ARRAY_SIZE(max1721x_deny_range), +}; + +/* + * Model Gauge M5 Algorithm output register + * Volatile data (must not be cached) + */ +static const struct regmap_range max1721x_volatile_allow[] = { + regmap_reg_range(0, 0xDF), +}; + +static const struct regmap_access_table max1721x_volatile_regs = { + .yes_ranges = max1721x_volatile_allow, + .n_yes_ranges = ARRAY_SIZE(max1721x_volatile_allow), +}; + +/* + * W1-regmap config + */ +static const struct regmap_config max1721x_regmap_w1_config = { + .reg_bits = 16, + .val_bits = 16, + .rd_table = &max1721x_regs, + .volatile_table = &max1721x_volatile_regs, + .max_register = MAX1721X_MAX_REG_NR, +}; + +static int devm_w1_max1721x_add_device(struct w1_slave *sl) +{ + struct power_supply_config psy_cfg = {}; + struct max17211_device_info *info; + + info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + sl->family_data = (void *)info; + info->w1_dev = &sl->dev; + + /* + * power_supply class battery name translated from W1 slave device + * unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0" + * so, 26 (device family) correcpondent to max1721x devices. + * Device name still unical for any numbers connected devices. + */ + snprintf(info->name, sizeof(info->name), + "max1721x-%012X", (unsigned int)sl->reg_num.id); + info->bat_desc.name = info->name; + + /* + * FixMe: battery device name exceed max len for thermal_zone device + * name and translation to thermal_zone must be disabled. + */ + info->bat_desc.no_thermal = true; + info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + info->bat_desc.properties = max1721x_battery_props; + info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props); + info->bat_desc.get_property = max1721x_battery_get_property; + psy_cfg.drv_data = info; + + /* regmap init */ + info->regmap = devm_regmap_init_w1(info->w1_dev, + &max1721x_regmap_w1_config); + if (IS_ERR(info->regmap)) { + int err = PTR_ERR(info->regmap); + + dev_err(info->w1_dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + /* rsense init */ + info->rsense = 0; + if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) { + dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n"); + return -ENODEV; + } + + if (!info->rsense) { + dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n"); + info->rsense = 1000; /* in regs in 10^-5 */ + } + dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100); + + if (get_string(info, MAX1721X_REG_MFG_STR, + MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) { + dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n"); + return -ENODEV; + } + + if (!info->ManufacturerName[0]) + strncpy(info->ManufacturerName, DEF_MFG_NAME, + 2 * MAX1721X_REG_MFG_NUMB); + + if (get_string(info, MAX1721X_REG_DEV_STR, + MAX1721X_REG_DEV_NUMB, info->DeviceName)) { + dev_err(info->w1_dev, "Can't read device. Hardware error.\n"); + return -ENODEV; + } + if (!info->DeviceName[0]) { + unsigned int dev_name; + + if (regmap_read(info->regmap, + MAX172XX_REG_DEVNAME, &dev_name)) { + dev_err(info->w1_dev, "Can't read device name reg.\n"); + return -ENODEV; + } + + switch (dev_name & MAX172XX_DEV_MASK) { + case MAX172X1_DEV: + strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211, + 2 * MAX1721X_REG_DEV_NUMB); + break; + case MAX172X5_DEV: + strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215, + 2 * MAX1721X_REG_DEV_NUMB); + break; + default: + strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN, + 2 * MAX1721X_REG_DEV_NUMB); + } + } + + if (get_sn_string(info, info->SerialNumber)) { + dev_err(info->w1_dev, "Can't read serial. Hardware error.\n"); + return -ENODEV; + } + + info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc, + &psy_cfg); + if (IS_ERR(info->bat)) { + dev_err(info->w1_dev, "failed to register battery\n"); + return PTR_ERR(info->bat); + } + + return 0; +} + +static struct w1_family_ops w1_max1721x_fops = { + .add_slave = devm_w1_max1721x_add_device, +}; + +static struct w1_family w1_max1721x_family = { + .fid = W1_MAX1721X_FAMILY_ID, + .fops = &w1_max1721x_fops, +}; + +module_w1_family(w1_max1721x_family); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alex A. Mihaylov <minimumlaw@rambler.ru>"); +MODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver"); +MODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID)); diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index 9e29b1321648..3bc2eea7b3b7 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -535,7 +535,7 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute olpc_bat_eeprom = { +static const struct bin_attribute olpc_bat_eeprom = { .attr = { .name = "eeprom", .mode = S_IRUGO, @@ -559,7 +559,7 @@ static ssize_t olpc_bat_error_read(struct device *dev, return sprintf(buf, "%d\n", ec_byte); } -static struct device_attribute olpc_bat_error = { +static const struct device_attribute olpc_bat_error = { .attr = { .name = "error", .mode = S_IRUGO, diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index b3c1873ad84d..1ad7ccce6075 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -254,7 +254,7 @@ static struct attribute *pcf50633_mbc_sysfs_entries[] = { NULL, }; -static struct attribute_group mbc_attr_group = { +static const struct attribute_group mbc_attr_group = { .name = NULL, /* put in device directory */ .attrs = pcf50633_mbc_sysfs_entries, }; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 540d3e0aa011..02c6340ae36f 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -259,18 +259,14 @@ static int power_supply_check_supplies(struct power_supply *psy) /* All supplies found, allocate char ** array for filling */ psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), GFP_KERNEL); - if (!psy->supplied_from) { - dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); + if (!psy->supplied_from) return -ENOMEM; - } *psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(char *) * (cnt - 1), GFP_KERNEL); - if (!*psy->supplied_from) { - dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); + if (!*psy->supplied_from) return -ENOMEM; - } return power_supply_populate_supplied_from(psy); } @@ -314,11 +310,12 @@ static int __power_supply_am_i_supplied(struct device *dev, void *_data) struct power_supply *epsy = dev_get_drvdata(dev); struct psy_am_i_supplied_data *data = _data; - data->count++; - if (__power_supply_is_supplied_by(epsy, data->psy)) + if (__power_supply_is_supplied_by(epsy, data->psy)) { + data->count++; if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; + } return 0; } @@ -374,6 +371,47 @@ 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) +{ + union power_supply_propval ret = {0,}; + struct power_supply *epsy = dev_get_drvdata(dev); + struct power_supply *psy = data; + + if (__power_supply_is_supplied_by(epsy, psy)) + if (!epsy->desc->get_property(epsy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &ret)) + return ret.intval; + + return 0; +} + +int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy) +{ + union power_supply_propval val = {0,}; + int curr; + + if (!psy->desc->set_property) + return -EINVAL; + + /* + * 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. + */ + 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; + + return psy->desc->set_property(psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); +} +EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier); + int power_supply_set_battery_charged(struct power_supply *psy) { if (atomic_read(&psy->use_cnt) >= 0 && diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f7059459f0fb..b19a73176910 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -12,25 +12,21 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/err.h> -#include <linux/power_supply.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/slab.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> -#include <linux/stat.h> - #include <linux/power/sbs-battery.h> +#include <linux/power_supply.h> +#include <linux/slab.h> +#include <linux/stat.h> enum { REG_MANUFACTURER_DATA, @@ -60,8 +56,8 @@ enum { #define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_MASK 0x8000 enum sbs_battery_mode { - BATTERY_MODE_AMPS, - BATTERY_MODE_WATTS + BATTERY_MODE_AMPS = 0, + BATTERY_MODE_WATTS = 0x8000 }; /* manufacturer access defines */ @@ -532,6 +528,8 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, if (ret < 0) return ret; + usleep_range(1000, 2000); + return original_val & BATTERY_MODE_MASK; } diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index bb4d7ed2ce55..3c945f9f5f0f 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -148,10 +148,4 @@ config W1_SLAVE_DS28E04 If you are unsure, say N. -config W1_SLAVE_BQ27000 - tristate "BQ27000 slave support" - help - Say Y here if you want to use a hdq - bq27000 slave support. - endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 4622d8fed362..36b22fb2d3a1 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -16,5 +16,4 @@ obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o -obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c deleted file mode 100644 index 8046ac45381a..000000000000 --- a/drivers/w1/slaves/w1_bq27000.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * drivers/w1/slaves/w1_bq27000.c - * - * Copyright (C) 2007 Texas Instruments, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/types.h> -#include <linux/platform_device.h> -#include <linux/mutex.h> -#include <linux/power/bq27xxx_battery.h> - -#include <linux/w1.h> - -#define W1_FAMILY_BQ27000 0x01 - -#define HDQ_CMD_READ (0) -#define HDQ_CMD_WRITE (1<<7) - -static int F_ID; -module_param(F_ID, int, S_IRUSR); -MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); - -static int w1_bq27000_read(struct device *dev, unsigned int reg) -{ - u8 val; - struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); - - mutex_lock(&sl->master->bus_mutex); - w1_write_8(sl->master, HDQ_CMD_READ | reg); - val = w1_read_8(sl->master); - mutex_unlock(&sl->master->bus_mutex); - - return val; -} - -static struct bq27xxx_platform_data bq27000_battery_info = { - .read = w1_bq27000_read, - .name = "bq27000-battery", - .chip = BQ27000, -}; - -static int w1_bq27000_add_slave(struct w1_slave *sl) -{ - int ret; - struct platform_device *pdev; - - pdev = platform_device_alloc("bq27000-battery", -1); - if (!pdev) { - ret = -ENOMEM; - return ret; - } - ret = platform_device_add_data(pdev, - &bq27000_battery_info, - sizeof(bq27000_battery_info)); - if (ret) - goto pdev_add_failed; - pdev->dev.parent = &sl->dev; - - ret = platform_device_add(pdev); - if (ret) - goto pdev_add_failed; - - dev_set_drvdata(&sl->dev, pdev); - - goto success; - -pdev_add_failed: - platform_device_put(pdev); -success: - return ret; -} - -static void w1_bq27000_remove_slave(struct w1_slave *sl) -{ - struct platform_device *pdev = dev_get_drvdata(&sl->dev); - - platform_device_unregister(pdev); -} - -static struct w1_family_ops w1_bq27000_fops = { - .add_slave = w1_bq27000_add_slave, - .remove_slave = w1_bq27000_remove_slave, -}; - -static struct w1_family w1_bq27000_family = { - .fid = W1_FAMILY_BQ27000, - .fops = &w1_bq27000_fops, -}; - -static int __init w1_bq27000_init(void) -{ - if (F_ID) - w1_bq27000_family.fid = F_ID; - - return w1_register_family(&w1_bq27000_family); -} - -static void __exit w1_bq27000_exit(void) -{ - w1_unregister_family(&w1_bq27000_family); -} - -module_init(w1_bq27000_init); -module_exit(w1_bq27000_exit); - -MODULE_AUTHOR("Texas Instruments Ltd"); -MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000)); diff --git a/include/linux/power/bq24190_charger.h b/include/linux/power/bq24190_charger.h new file mode 100644 index 000000000000..45ce7f116a91 --- /dev/null +++ b/include/linux/power/bq24190_charger.h @@ -0,0 +1,18 @@ +/* + * Platform data for the TI bq24190 battery charger driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BQ24190_CHARGER_H_ +#define _BQ24190_CHARGER_H_ + +#include <linux/regulator/machine.h> + +struct bq24190_platform_data { + const struct regulator_init_data *regulator_init_data; +}; + +#endif diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index 11e11685dd1d..43194e02c1ee 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -6,6 +6,7 @@ enum bq27xxx_chip { BQ27010, /* bq27010, bq27210 */ BQ2750X, /* bq27500 deprecated alias */ BQ2751X, /* bq27510, bq27520 deprecated alias */ + BQ2752X, BQ27500, /* bq27500/1 */ BQ27510G1, /* bq27510G1 */ BQ27510G2, /* bq27510G2 */ @@ -15,26 +16,16 @@ enum bq27xxx_chip { BQ27520G3, /* bq27520G3 */ BQ27520G4, /* bq27520G4 */ BQ27530, /* bq27530, bq27531 */ + BQ27531, BQ27541, /* bq27541, bq27542, bq27546, bq27742 */ + BQ27542, + BQ27546, + BQ27742, BQ27545, /* bq27545 */ BQ27421, /* bq27421, bq27425, bq27441, bq27621 */ -}; - -/** - * struct bq27xxx_plaform_data - Platform data for bq27xxx devices - * @name: Name of the battery. - * @chip: Chip class number of this device. - * @read: HDQ read callback. - * This function should provide access to the HDQ bus the battery is - * connected to. - * The first parameter is a pointer to the battery device, the second the - * register to be read. The return value should either be the content of - * the passed register or an error value. - */ -struct bq27xxx_platform_data { - const char *name; - enum bq27xxx_chip chip; - int (*read)(struct device *dev, unsigned int); + BQ27425, + BQ27441, + BQ27621, }; struct bq27xxx_device_info; @@ -63,7 +54,7 @@ struct bq27xxx_device_info { struct device *dev; int id; enum bq27xxx_chip chip; - bool ram_chip; + u32 opts; const char *name; struct bq27xxx_dm_reg *dm_regs; u32 unseal_key; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index de89066b72b1..79e90b3d3288 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -332,6 +332,8 @@ extern int power_supply_get_battery_info(struct power_supply *psy, struct power_supply_battery_info *info); 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); extern int power_supply_set_battery_charged(struct power_supply *psy); #ifdef CONFIG_POWER_SUPPLY |