Commit 449dc8c9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset updates from Sebastian Reichel:
 "Power-supply core:
   - add COOL/WARM/HOT state from JEITA JISC8712:2015 specification
   - convert simple-battery DT binding to YAML
   - add long-life charging mode

 Battery/charger drivers:
   - bq25150: new charger driver
   - bq27xxx: add support for BQ27z561 and BQ28z610
   - max17040: support CAPACITY_ALERT_MIN
   - sbs-battery: add PEC support
   - wilco-ec: support long-life charging mode
   - bq25890: fix DT binding
   - misc. fixes and cleanups

 Reset drivers:
   - linkstation: new reset driver"

* tag 'for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (32 commits)
  power: supply: wilco_ec: Add long life charging mode
  power: supply: bq27xxx_battery: Add the BQ28z610 Battery monitor
  dt-bindings: power: Add BQ28z610 compatible
  power: supply: bq27xxx_battery: Add the BQ27Z561 Battery monitor
  dt-bindings: power: Add BQ27Z561 compatible
  power: supply: test_power: Fix battery_current initial value
  power: supply: Fix kerneldoc of power_supply_temp2resist_simple()
  power: supply: cpcap-battery: Fix kerneldoc of cpcap_battery_read_accumulated()
  dt-bindings: power: Convert battery.txt to battery.yaml
  power: supply: rt5033_battery: Fix error code in rt5033_battery_probe()
  power: supply: max17040: Add POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN
  power: supply: check if calc_soc succeeded in pm860x_init_battery
  power: supply: bq2xxxx: Replace HTTP links with HTTPS ones
  power: reset: add driver for LinkStation power off
  power: supply: sc27xx: prevent adc * 1000 from overflow
  math64: New DIV_S64_ROUND_CLOSEST helper
  power: fix duplicated words in bq2415x_charger.h
  power: Convert to DEFINE_SHOW_ATTRIBUTE
  power: reset: keystone-reset: Replace HTTP links with HTTPS ones
  power: supply: bq25150 introduce the bq25150
  ...
parents b79675e1 46cbd0b0
......@@ -205,7 +205,8 @@ Description:
Valid values: "Unknown", "Good", "Overheat", "Dead",
"Over voltage", "Unspecified failure", "Cold",
"Watchdog timer expire", "Safety timer expire",
"Over current", "Calibration required"
"Over current", "Calibration required", "Warm",
"Cool", "Hot"
What: /sys/class/power_supply/<supply_name>/precharge_current
Date: June 2017
......
......@@ -14,6 +14,10 @@ Description:
Charging begins when level drops below
charge_control_start_threshold, and ceases when
level is above charge_control_end_threshold.
Long Life: Customized charge rate for last longer battery life.
On Wilco device this mode is pre-configured in the factory
through EC's private PID. Swiching to a different mode will
be denied by Wilco EC when Long Life mode is enabled.
What: /sys/class/power_supply/wilco-charger/charge_control_start_threshold
Date: April 2019
......
Battery Characteristics
The devicetree battery node provides static battery characteristics.
In smart batteries, these are typically stored in non-volatile memory
on a fuel gauge chip. The battery node should be used where there is
no appropriate non-volatile memory, or it is unprogrammed/incorrect.
Upstream dts files should not include battery nodes, unless the battery
represented cannot easily be replaced in the system by one of a
different type. This prevents unpredictable, potentially harmful,
behavior should a replacement that changes the battery type occur
without a corresponding update to the dtb.
The contents of this file has been moved to battery.yaml
Please note that not all charger drivers respect all of the properties.
Required Properties:
- compatible: Must be "simple-battery"
Optional Properties:
- over-voltage-threshold-microvolt: battery over-voltage limit
- re-charge-voltage-microvolt: limit to automatically start charging again
- voltage-min-design-microvolt: drained battery voltage
- voltage-max-design-microvolt: fully charged battery voltage
- energy-full-design-microwatt-hours: battery design energy
- charge-full-design-microamp-hours: battery design capacity
- trickle-charge-current-microamp: current for trickle-charge phase
- precharge-current-microamp: current for pre-charge phase
- precharge-upper-limit-microvolt: limit when to change to constant charging
- charge-term-current-microamp: current for charge termination phase
- constant-charge-current-max-microamp: maximum constant input current
- constant-charge-voltage-max-microvolt: maximum constant input voltage
- factory-internal-resistance-micro-ohms: battery factory internal resistance
- ocv-capacity-table-0: An array providing the open circuit voltage (OCV)
of the battery and corresponding battery capacity percent, which is used
to look up battery capacity according to current OCV value. And the open
circuit voltage unit is microvolt.
- ocv-capacity-table-1: Same as ocv-capacity-table-0
......
- ocv-capacity-table-n: Same as ocv-capacity-table-0
- ocv-capacity-celsius: An array containing the temperature in degree Celsius,
for each of the battery capacity lookup table. The first temperature value
specifies the OCV table 0, and the second temperature value specifies the
OCV table 1, and so on.
- resistance-temp-table: An array providing the temperature in degree Celsius
and corresponding battery internal resistance percent, which is used to look
up the resistance percent according to current temperature to get a accurate
batterty internal resistance in different temperatures.
Battery properties are named, where possible, for the corresponding
elements in enum power_supply_property, defined in
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/power_supply.h
Batteries must be referenced by chargers and/or fuel-gauges
using a phandle. The phandle's property should be named
"monitored-battery".
Example:
bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3200000>;
voltage-max-design-microvolt = <4200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
precharge-current-microamp = <256000>;
charge-term-current-microamp = <128000>;
constant-charge-current-max-microamp = <900000>;
constant-charge-voltage-max-microvolt = <4200000>;
factory-internal-resistance-micro-ohms = <250000>;
ocv-capacity-celsius = <(-10) 0 10>;
ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...;
ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...;
ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...;
resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>;
};
charger: charger@11 {
....
monitored-battery = <&bat>;
...
};
fuel_gauge: fuel-gauge@22 {
....
monitored-battery = <&bat>;
...
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/battery.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Battery Characteristics
maintainers:
- Sebastian Reichel <sre@kernel.org>
description: |
The devicetree battery node provides static battery characteristics.
In smart batteries, these are typically stored in non-volatile memory
on a fuel gauge chip. The battery node should be used where there is
no appropriate non-volatile memory, or it is unprogrammed/incorrect.
Upstream dts files should not include battery nodes, unless the battery
represented cannot easily be replaced in the system by one of a
different type. This prevents unpredictable, potentially harmful,
behavior should a replacement that changes the battery type occur
without a corresponding update to the dtb.
Battery properties are named, where possible, for the corresponding elements
in enum power_supply_property, defined in include/linux/power_supply.h
Batteries must be referenced by chargers and/or fuel-gauges using a phandle.
The phandle's property should be named "monitored-battery".
properties:
compatible:
const: simple-battery
over-voltage-threshold-microvolt:
description: battery over-voltage limit
re-charge-voltage-microvolt:
description: limit to automatically start charging again
voltage-min-design-microvolt:
description: drained battery voltage
voltage-max-design-microvolt:
description: fully charged battery voltage
energy-full-design-microwatt-hours:
description: battery design energy
charge-full-design-microamp-hours:
description: battery design capacity
trickle-charge-current-microamp:
description: current for trickle-charge phase
precharge-current-microamp:
description: current for pre-charge phase
precharge-upper-limit-microvolt:
description: limit when to change to constant charging
charge-term-current-microamp:
description: current for charge termination phase
constant-charge-current-max-microamp:
description: maximum constant input current
constant-charge-voltage-max-microvolt:
description: maximum constant input voltage
factory-internal-resistance-micro-ohms:
description: battery factory internal resistance
resistance-temp-table:
description: |
An array providing the temperature in degree Celsius
and corresponding battery internal resistance percent, which is used to
look up the resistance percent according to current temperature to get an
accurate batterty internal resistance in different temperatures.
ocv-capacity-celsius:
description: |
An array containing the temperature in degree Celsius,
for each of the battery capacity lookup table.
required:
- compatible
patternProperties:
'^ocv-capacity-table-[0-9]+$':
$ref: /schemas/types.yaml#/definitions/uint32-matrix
description: |
An array providing the open circuit voltage (OCV)
of the battery and corresponding battery capacity percent, which is used
to look up battery capacity according to current OCV value. And the open
circuit voltage unit is microvolt.
maxItems: 100
items:
items:
- description: open circuit voltage (OCV) in microvolts
- description: battery capacity percent
maximum: 100
additionalProperties: false
examples:
- |
power {
#address-cells = <1>;
#size-cells = <0>;
battery: battery {
compatible = "simple-battery";
over-voltage-threshold-microvolt = <4500000>;
re-charge-voltage-microvolt = <250000>;
voltage-min-design-microvolt = <3200000>;
voltage-max-design-microvolt = <4200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
precharge-current-microamp = <256000>;
precharge-upper-limit-microvolt = <2500000>;
charge-term-current-microamp = <128000>;
constant-charge-current-max-microamp = <900000>;
constant-charge-voltage-max-microvolt = <4200000>;
factory-internal-resistance-micro-ohms = <250000>;
ocv-capacity-celsius = <(-10) 0 10>;
/* table for -10 degree Celsius */
ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>;
/* table for 0 degree Celsius */
ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>;
/* table for 10 degree Celsius */
ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>;
resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>;
};
charger@11 {
reg = <0x11>;
monitored-battery = <&battery>;
};
fuel-gauge@22 {
reg = <0x22>;
monitored-battery = <&battery>;
};
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright (C) 2020 Texas Instruments Incorporated
%YAML 1.2
---
$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: TI bq2515x 500-mA Linear charger family
maintainers:
- Dan Murphy <dmurphy@ti.com>
- Ricardo Rivera-Matos <r-rivera-matos@ti.com>
description: |
The BQ2515x family is a highly integrated battery charge management IC that
integrates the most common functions for wearable devices, namely a charger,
an output voltage rail, ADC for battery and system monitoring, and
push-button controller.
Specifications about the charger can be found at:
http://www.ti.com/lit/ds/symlink/bq25150.pdf
http://www.ti.com/lit/ds/symlink/bq25155.pdf
properties:
compatible:
enum:
- ti,bq25150
- ti,bq25155
reg:
maxItems: 1
description: I2C address of the charger.
ac-detect-gpios:
description: |
GPIO used for connecting the bq2515x device PG (AC Detect)
pin.
maxItems: 1
reset-gpios:
description: GPIO used for hardware reset.
maxItems: 1
powerdown-gpios:
description: GPIO used for low power mode of IC.
maxItems: 1
charge-enable-gpios:
description: GPIO used to turn on and off charging.
maxItems: 1
input-current-limit-microamp:
$ref: /schemas/types.yaml#/definitions/uint32
description: Maximum input current in micro Amps.
minimum: 50000
maximum: 500000
monitored-battery:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle to the battery node being monitored
required:
- compatible
- reg
- monitored-battery
additionalProperties: false
examples:
- |
bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <50000>;
precharge-current-microamp = <2500>;
constant-charge-voltage-max-microvolt = <4000000>;
};
#include <dt-bindings/gpio/gpio.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
bq25150: charger@6b {
compatible = "ti,bq25150";
reg = <0x6b>;
monitored-battery = <&bat>;
input-current-limit-microamp = <100000>;
ac-detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
powerdown-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
};
......@@ -10,6 +10,7 @@ Required properties:
* "ti,bq25895"
* "ti,bq25896"
- reg: integer, i2c address of the device.
- interrupts: interrupt line;
- ti,battery-regulation-voltage: integer, maximum charging voltage (in uV);
- ti,charge-current: integer, maximum charging current (in uA);
- ti,termination-current: integer, charge will be terminated when current in
......@@ -36,17 +37,20 @@ Optional properties:
Example:
bq25890 {
compatible = "ti,bq25890";
reg = <0x6a>;
ti,battery-regulation-voltage = <4200000>;
ti,charge-current = <1000000>;
ti,termination-current = <50000>;
ti,precharge-current = <128000>;
ti,minimum-sys-voltage = <3600000>;
ti,boost-voltage = <5000000>;
ti,boost-max-current = <1000000>;
ti,use-ilim-pin;
ti,thermal-regulation-threshold = <120>;
compatible = "ti,bq25890";
reg = <0x6a>;
interrupt-parent = <&gpio1>;
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
ti,battery-regulation-voltage = <4200000>;
ti,charge-current = <1000000>;
ti,termination-current = <50000>;
ti,precharge-current = <128000>;
ti,minimum-sys-voltage = <3600000>;
ti,boost-voltage = <5000000>;
ti,boost-max-current = <1000000>;
ti,use-ilim-pin;
ti,thermal-regulation-threshold = <120>;
};
......@@ -49,6 +49,8 @@ properties:
- ti,bq27426
- ti,bq27441
- ti,bq27621
- ti,bq27z561
- ti,bq28z610
reg:
maxItems: 1
......
gpio-charger
Required properties :
- compatible : "gpio-charger"
- gpios : GPIO indicating the charger presence.
See GPIO binding in bindings/gpio/gpio.txt .
- charger-type : power supply type, one of
unknown
battery
ups
mains
usb-sdp (USB standard downstream port)
usb-dcp (USB dedicated charging port)
usb-cdp (USB charging downstream port)
usb-aca (USB accessory charger adapter)
Optional properties:
- charge-status-gpios: GPIO indicating whether a battery is charging.
Example:
usb_charger: charger {
compatible = "gpio-charger";
charger-type = "usb-sdp";
gpios = <&gpd 28 GPIO_ACTIVE_LOW>;
charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>;
};
battery {
power-supplies = <&usb_charger>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/gpio-charger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: simple battery chargers only communicating through GPIOs
maintainers:
- Sebastian Reichel <sre@kernel.org>
description:
This binding is for all chargers, which are working more or less
autonomously, only providing some status GPIOs and possibly some
GPIOs for limited control over the charging process.
properties:
compatible:
const: gpio-charger
charger-type:
enum:
- unknown
- battery
- ups
- mains
- usb-sdp # USB standard downstream port
- usb-dcp # USB dedicated charging port
- usb-cdp # USB charging downstream port
- usb-aca # USB accessory charger adapter
description:
Type of the charger, e.g. "mains" for a wall charger.
gpios:
maxItems: 1
description: GPIO indicating the charger presence
charge-status-gpios:
maxItems: 1
description: GPIO indicating the charging status
required:
- compatible
anyOf:
- required:
- gpios
- required:
- charge-status-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
charger {
compatible = "gpio-charger";
charger-type = "usb-sdp";
gpios = <&gpd 28 GPIO_ACTIVE_LOW>;
charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>;
};
......@@ -99,6 +99,17 @@ config POWER_RESET_HISI
help
Reboot support for Hisilicon boards.
config POWER_RESET_LINKSTATION
tristate "Buffalo LinkStation power-off driver"
depends on ARCH_MVEBU || COMPILE_TEST
depends on OF_MDIO && PHYLIB
help
This driver supports turning off some Buffalo LinkStations by
setting an output pin at the ethernet PHY to the correct state.
It also makes the device compatible with the WoL function.
Say Y here if you have a Buffalo LinkStation LS421D/E.
config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver"
depends on ARCH_QCOM
......
......@@ -10,6 +10,7 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-${CONFIG_POWER_RESET_LINKSTATION} += linkstation-poweroff.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o
......
......@@ -2,7 +2,7 @@
/*
* TI keystone reboot driver
*
* Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/
* Copyright (C) 2014 Texas Instruments Incorporated. https://www.ti.com/
*
* Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
*/
......
// SPDX-License-Identifier: GPL-2.0
/*
* LinkStation power off restart driver
* Copyright (C) 2020 Daniel González Cabanelas <dgcbueu@gmail.com>
*/
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/reboot.h>
#include <linux/phy.h>
/* Defines from the eth phy Marvell driver */
#define MII_MARVELL_COPPER_PAGE 0
#define MII_MARVELL_LED_PAGE 3
#define MII_MARVELL_WOL_PAGE 17
#define MII_MARVELL_PHY_PAGE 22
#define MII_PHY_LED_CTRL 16
#define MII_88E1318S_PHY_LED_TCR 18
#define MII_88E1318S_PHY_WOL_CTRL 16
#define MII_M1011_IEVENT 19
#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12)
#define LED2_FORCE_ON (0x8 << 8)
#define LEDMASK GENMASK(11,8)
static struct phy_device *phydev;
static void mvphy_reg_intn(u16 data)
{
int rc = 0, saved_page;
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
if (saved_page < 0)
goto err;
/* Force manual LED2 control to let INTn work */
__phy_modify(phydev, MII_PHY_LED_CTRL, LEDMASK, LED2_FORCE_ON);
/* Set the LED[2]/INTn pin to the required state */
__phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
MII_88E1318S_PHY_LED_TCR_FORCE_INT,
MII_88E1318S_PHY_LED_TCR_INTn_ENABLE | data);
if (!data) {
/* Clear interrupts to ensure INTn won't be holded in high state */
__phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_COPPER_PAGE);
__phy_read(phydev, MII_M1011_IEVENT);
/* If WOL was enabled and a magic packet was received before powering
* off, we won't be able to wake up by sending another magic packet.
* Clear WOL status.
*/
__phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE);
__phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
}
err:
rc = phy_restore_page(phydev, saved_page, rc);
if (rc < 0)
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
}
static int linkstation_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused)
{
if (action == SYS_RESTART)
mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT);
return NOTIFY_DONE;
}
static struct notifier_block linkstation_reboot_nb = {
.notifier_call = linkstation_reboot_notifier,
};
static void linkstation_poweroff(void)
{
unregister_reboot_notifier(&linkstation_reboot_nb);
mvphy_reg_intn(0);
kernel_restart("Power off");
}
static const struct of_device_id ls_poweroff_of_match[] = {
{ .compatible = "buffalo,ls421d" },
{ .compatible = "buffalo,ls421de" },
{ },
};
static int __init linkstation_poweroff_init(void)
{
struct mii_bus *bus;
struct device_node *dn;
dn = of_find_matching_node(NULL, ls_poweroff_of_match);
if (!dn)
return -ENODEV;
of_node_put(dn);
dn = of_find_node_by_name(NULL, "mdio");
if (!dn)
return -ENODEV;
bus = of_mdio_find_bus(dn);
of_node_put(dn);
if (!bus)
return -EPROBE_DEFER;
phydev = phy_find_first(bus);
if (!phydev)
return -EPROBE_DEFER;
register_reboot_notifier(&linkstation_reboot_nb);
pm_power_off = linkstation_poweroff;
return 0;
}
static void __exit linkstation_poweroff_exit(void)
{
pm_power_off = NULL;
unregister_reboot_notifier(&linkstation_reboot_nb);
}
module_init(linkstation_poweroff_init);
module_exit(linkstation_poweroff_exit);
MODULE_AUTHOR("Daniel González Cabanelas <dgcbueu@gmail.com>");
MODULE_DESCRIPTION("LinkStation power off driver");
MODULE_LICENSE("GPL v2");
......@@ -433,7 +433,7 @@ static void pm860x_init_battery(struct pm860x_battery_info *info)
int ret;
int data;
int bat_remove;
int soc;
int soc = 0;
/* measure enable on GPADC1 */
data = MEAS1_GP1;
......@@ -496,7 +496,9 @@ static void pm860x_init_battery(struct pm860x_battery_info *info)
}
mutex_unlock(&info->lock);
calc_soc(info, OCV_MODE_ACTIVE, &soc);
ret = calc_soc(info, OCV_MODE_ACTIVE, &soc);
if (ret < 0)
goto out;
data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG);
bat_remove = data & BAT_WU_LOG;
......
......@@ -610,6 +610,19 @@ config CHARGER_BQ24735
help
Say Y to enable support for the TI BQ24735 battery charger.
config CHARGER_BQ2515X
tristate "TI BQ2515X battery charger family"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select REGMAP_I2C
help
Say Y to enable support for the TI BQ2515X family of battery
charging integrated circuits. The BQ2515X are highly integrated
battery charge management ICs that integrate the most common
functions for wearable devices, namely a charger, an output voltage
rail, ADC for battery and system monitoring, and push-button
controller.
config CHARGER_BQ25890
tristate "TI BQ25890 battery charger driver"
depends on I2C
......
......@@ -82,6 +82,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o
obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o
obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o
obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
......
......@@ -78,7 +78,7 @@ static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power)
/*
* Polling is only necessary while VBUS is offline. While online, a
* present->absent transition implies an online->offline transition
* and will triger the VBUS_REMOVAL IRQ.
* and will trigger the VBUS_REMOVAL IRQ.
*/
if (power->axp20x_id >= AXP221_ID && !power->online)
return true;
......
......@@ -5,14 +5,14 @@
* Copyright (C) 2011-2013 Pali Rohár <pali@kernel.org>
*
* Datasheets:
* http://www.ti.com/product/bq24150
* http://www.ti.com/product/bq24150a
* http://www.ti.com/product/bq24152
* http://www.ti.com/product/bq24153
* http://www.ti.com/product/bq24153a
* http://www.ti.com/product/bq24155
* http://www.ti.com/product/bq24157s
* http://www.ti.com/product/bq24158
* https://www.ti.com/product/bq24150
* https://www.ti.com/product/bq24150a
* https://www.ti.com/product/bq24152
* https://www.ti.com/product/bq24153
* https://www.ti.com/product/bq24153a
* https://www.ti.com/product/bq24155
* https://www.ti.com/product/bq24157s
* https://www.ti.com/product/bq24158
*/
#include <linux/kernel.h>
......
......@@ -481,8 +481,10 @@ static ssize_t bq24190_sysfs_store(struct device *dev,
return ret;
ret = pm_runtime_get_sync(bdi->dev);
if (ret < 0)
if (ret < 0) {
pm_runtime_put_noidle(bdi->dev);
return ret;
}
ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v);
if (ret)
......
......@@ -5,9 +5,9 @@
* Copyright (C) 2015 Intel Corporation
*
* Datasheets:
* http://www.ti.com/product/bq24250
* http://www.ti.com/product/bq24251
* http://www.ti.com/product/bq24257
* https://www.ti.com/product/bq24250
* https://www.ti.com/product/bq24251
* https://www.ti.com/product/bq24257
*/
#include <linux/module.h>
......
// SPDX-License-Identifier: GPL-2.0
// BQ2515X Battery Charger Driver
// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/types.h>
#define BQ2515X_MANUFACTURER "Texas Instruments"
#define BQ2515X_STAT0 0x00
#define BQ2515X_STAT1 0x01
#define BQ2515X_STAT2 0x02
#define BQ2515X_FLAG0 0x03
#define BQ2515X_FLAG1 0x04
#define BQ2515X_FLAG2 0x05
#define BQ2515X_FLAG3 0x06
#define BQ2515X_MASK0 0x07
#define BQ2515X_MASK1 0x08
#define BQ2515X_MASK2 0x09
#define BQ2515X_MASK3 0x0a
#define BQ2515X_VBAT_CTRL 0x12
#define BQ2515X_ICHG_CTRL 0x13
#define BQ2515X_PCHRGCTRL 0x14
#define BQ2515X_TERMCTRL 0x15
#define BQ2515X_BUVLO 0x16
#define BQ2515X_CHARGERCTRL0 0x17
#define BQ2515X_CHARGERCTRL1 0x18
#define BQ2515X_ILIMCTRL 0x19
#define BQ2515X_LDOCTRL 0x1d
#define BQ2515X_MRCTRL 0x30
#define BQ2515X_ICCTRL0 0x35
#define BQ2515X_ICCTRL1 0x36
#define BQ2515X_ICCTRL2 0x37
#define BQ2515X_ADCCTRL0 0x40
#define BQ2515X_ADCCTRL1 0x41
#define BQ2515X_ADC_VBAT_M 0x42
#define BQ2515X_ADC_VBAT_L 0x43
#define BQ2515X_ADC_TS_M 0x44
#define BQ2515X_ADC_TS_L 0x45
#define BQ2515X_ADC_ICHG_M 0x46
#define BQ2515X_ADC_ICHG_L 0x47
#define BQ2515X_ADC_ADCIN_M 0x48
#define BQ2515X_ADC_ADCIN_L 0x49
#define BQ2515X_ADC_VIN_M 0x4a
#define BQ2515X_ADC_VIN_L 0x4b
#define BQ2515X_ADC_PMID_M 0x4c
#define BQ2515X_ADC_PMID_L 0x4d
#define BQ2515X_ADC_IIN_M 0x4e
#define BQ2515X_ADC_IIN_L 0x4f
#define BQ2515X_ADC_COMP1_M 0x52
#define BQ2515X_ADC_COMP1_L 0X53
#define BQ2515X_ADC_COMP2_M 0X54
#define BQ2515X_ADC_COMP2_L 0x55
#define BQ2515X_ADC_COMP3_M 0x56
#define BQ2515X_ADC_COMP3_L 0x57
#define BQ2515X_ADC_READ_EN 0x58
#define BQ2515X_TS_FASTCHGCTRL 0x61
#define BQ2515X_TS_COLD 0x62
#define BQ2515X_TS_COOL 0x63
#define BQ2515X_TS_WARM 0x64
#define BQ2515X_TS_HOT 0x65
#define BQ2515X_DEVICE_ID 0x6f
#define BQ2515X_DEFAULT_ICHG_UA 10000
#define BQ25150_DEFAULT_ILIM_UA 100000
#define BQ25155_DEFAULT_ILIM_UA 500000
#define BQ2515X_DEFAULT_VBAT_REG_UV 4200000
#define BQ2515X_DEFAULT_IPRECHARGE_UA 2500
#define BQ2515X_DIVISOR 65536
#define BQ2515X_VBAT_BASE_VOLT 3600000
#define BQ2515X_VBAT_REG_MAX 4600000
#define BQ2515X_VBAT_REG_MIN 3600000
#define BQ2515X_VBAT_STEP_UV 10000
#define BQ2515X_UV_FACTOR 1000000
#define BQ2515X_VBAT_MULTIPLIER 6
#define BQ2515X_ICHG_DIVISOR 52429
#define BQ2515X_ICHG_CURR_STEP_THRESH_UA 318750
#define BQ2515X_ICHG_MIN_UA 0
#define BQ2515X_ICHG_MAX_UA 500000
#define BQ2515X_ICHG_RNG_1B0_UA 1250
#define BQ2515X_ICHG_RNG_1B1_UA 2500
#define BQ2515X_VLOWV_SEL_1B0_UV 3000000
#define BQ2515X_VLOWV_SEL_1B1_UV 2800000
#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA 18750
#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA 37500
#define BQ2515X_TWAKE2_MIN_US 1700000
#define BQ2515X_TWAKE2_MAX_US 2300000
#define BQ2515X_ILIM_150MA 0x2
#define BQ2515X_ILIM_MASK 0x7
#define BQ2515X_ILIM_MIN 50000
#define BQ2515X_ILIM_MAX 600000
#define BQ2515X_HEALTH_MASK 0xf
#define BQ2515X_ICHGRNG_MASK 0x80
#define BQ2515X_STAT0_MASK 0x0f
#define BQ2515X_STAT1_MASK 0x1f
#define BQ2515X_PRECHARGE_MASK 0x1f
#define BQ2515X_TS_HOT_STAT BIT(0)
#define BQ2515X_TS_WARM_STAT BIT(1)
#define BQ2515X_TS_COOL_STAT BIT(2)
#define BQ2515X_TS_COLD_STAT BIT(3)
#define BQ2515X_SAFETY_TIMER_EXP BIT(5)
#define BQ2515X_EN_VBAT_READ BIT(3)
#define BQ2515X_EN_ICHG_READ BIT(5)
#define BQ2515X_VIN_GOOD BIT(0)
#define BQ2515X_CHRG_DONE BIT(5)
#define BQ2515X_CV_CHRG_MODE BIT(6)
#define BQ2515X_VIN_OVP_FAULT_STAT BIT(7)
#define BQ2515X_WATCHDOG_DISABLE BIT(4)
#define BQ2515X_ICHARGE_RANGE BIT(7)
#define BQ2515X_VLOWV_SEL BIT(5)
#define BQ2515X_CHARGER_DISABLE BIT(0)
#define BQ2515X_HWRESET_14S_WD BIT(1)
static const int bq2515x_ilim_lvl_values[] = {
50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000
};
/**
* struct bq2515x_init_data -
* @ilim: input current limit
* @ichg: fast charge current
* @vbatreg: battery regulation voltage
* @iprechg: precharge current
*/
struct bq2515x_init_data {
int ilim;
int ichg;
int vbatreg;
int iprechg;
};
enum bq2515x_id {
BQ25150,
BQ25155,
};
/**
* struct bq2515x_device -
* @mains: mains properties
* @battery: battery properties
* @regmap: register map structure
* @dev: device structure
*
* @reset_gpio: manual reset (MR) pin
* @powerdown_gpio: low power mode pin
* @ac_detect_gpio: power good (PG) pin
* @ce_gpio: charge enable (CE) pin
*
* @model_name: string value describing device model
* @device_id: value of device_id
* @mains_online: boolean value indicating power supply online
*
* @bq2515x_init_data init_data: charger initialization data structure
*/
struct bq2515x_device {
struct power_supply *mains;
struct power_supply *battery;
struct regmap *regmap;
struct device *dev;
struct gpio_desc *reset_gpio;
struct gpio_desc *powerdown_gpio;
struct gpio_desc *ac_detect_gpio;
struct gpio_desc *ce_gpio;
char model_name[I2C_NAME_SIZE];
int device_id;
bool mains_online;
struct bq2515x_init_data init_data;
};
static struct reg_default bq25150_reg_defaults[] = {
{BQ2515X_FLAG0, 0x0},
{BQ2515X_FLAG1, 0x0},
{BQ2515X_FLAG2, 0x0},
{BQ2515X_FLAG3, 0x0},
{BQ2515X_MASK0, 0x0},
{BQ2515X_MASK1, 0x0},
{BQ2515X_MASK2, 0x71},
{BQ2515X_MASK3, 0x0},
{BQ2515X_VBAT_CTRL, 0x3C},
{BQ2515X_ICHG_CTRL, 0x8},
{BQ2515X_PCHRGCTRL, 0x2},
{BQ2515X_TERMCTRL, 0x14},
{BQ2515X_BUVLO, 0x0},
{BQ2515X_CHARGERCTRL0, 0x82},
{BQ2515X_CHARGERCTRL1, 0x42},
{BQ2515X_ILIMCTRL, 0x1},
{BQ2515X_LDOCTRL, 0xB0},
{BQ2515X_MRCTRL, 0x2A},
{BQ2515X_ICCTRL0, 0x10},
{BQ2515X_ICCTRL1, 0x0},
{BQ2515X_ICCTRL2, 0x0},
{BQ2515X_ADCCTRL0, 0x2},
{BQ2515X_ADCCTRL1, 0x40},
{BQ2515X_ADC_COMP1_M, 0x23},
{BQ2515X_ADC_COMP1_L, 0x20},
{BQ2515X_ADC_COMP2_M, 0x38},
{BQ2515X_ADC_COMP2_L, 0x90},
{BQ2515X_ADC_COMP3_M, 0x0},
{BQ2515X_ADC_COMP3_L, 0x0},
{BQ2515X_ADC_READ_EN, 0x0},
{BQ2515X_TS_FASTCHGCTRL, 0x34},
{BQ2515X_TS_COLD, 0x7C},
{BQ2515X_TS_COOL, 0x6D},
{BQ2515X_TS_WARM, 0x38},
{BQ2515X_TS_HOT, 0x27},
{BQ2515X_DEVICE_ID, 0x20},
};
static struct reg_default bq25155_reg_defaults[] = {
{BQ2515X_FLAG0, 0x0},
{BQ2515X_FLAG1, 0x0},
{BQ2515X_FLAG2, 0x0},
{BQ2515X_FLAG3, 0x0},
{BQ2515X_MASK0, 0x0},
{BQ2515X_MASK1, 0x0},
{BQ2515X_MASK2, 0x71},
{BQ2515X_MASK3, 0x0},
{BQ2515X_VBAT_CTRL, 0x3C},
{BQ2515X_ICHG_CTRL, 0x8},
{BQ2515X_PCHRGCTRL, 0x2},
{BQ2515X_TERMCTRL, 0x14},
{BQ2515X_BUVLO, 0x0},
{BQ2515X_CHARGERCTRL0, 0x82},
{BQ2515X_CHARGERCTRL1, 0xC2},
{BQ2515X_ILIMCTRL, 0x6},
{BQ2515X_LDOCTRL, 0xB0},
{BQ2515X_MRCTRL, 0x2A},
{BQ2515X_ICCTRL0, 0x10},
{BQ2515X_ICCTRL1, 0x0},
{BQ2515X_ICCTRL2, 0x40},
{BQ2515X_ADCCTRL0, 0x2},
{BQ2515X_ADCCTRL1, 0x40},
{BQ2515X_ADC_COMP1_M, 0x23},
{BQ2515X_ADC_COMP1_L, 0x20},
{BQ2515X_ADC_COMP2_M, 0x38},
{BQ2515X_ADC_COMP2_L, 0x90},
{BQ2515X_ADC_COMP3_M, 0x0},
{BQ2515X_ADC_COMP3_L, 0x0},
{BQ2515X_ADC_READ_EN, 0x0},
{BQ2515X_TS_FASTCHGCTRL, 0x34},
{BQ2515X_TS_COLD, 0x7C},
{BQ2515X_TS_COOL, 0x6D},
{BQ2515X_TS_WARM, 0x38},
{BQ2515X_TS_HOT, 0x27},
{BQ2515X_DEVICE_ID, 0x35},
};
static int bq2515x_wake_up(struct bq2515x_device *bq2515x)
{
int ret;
int val;
/* Read the STAT register if we can read it then the device is out
* of ship mode. If the register cannot be read then attempt to wake
* it up and enable the ADC.
*/
ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
if (ret)
return ret;
/* Need to toggle LP and bring device out of ship mode. The device
* will exit the ship mode when the MR pin is held low for at least
* t_WAKE2 as shown in section 8.3.7.1 of the datasheet.
*/
gpiod_set_value_cansleep(bq2515x->powerdown_gpio, 0);
gpiod_set_value_cansleep(bq2515x->reset_gpio, 0);
usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US);
gpiod_set_value_cansleep(bq2515x->reset_gpio, 1);
return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN,
(BQ2515X_EN_VBAT_READ | BQ2515X_EN_ICHG_READ));
}
static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x)
{
bool dc = false;
unsigned int val;
int ret;
if (bq2515x->ac_detect_gpio)
val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio);
else {
ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
if (ret)
return ret;
}
dc = val & BQ2515X_VIN_GOOD;
ret = bq2515x->mains_online != dc;
bq2515x->mains_online = dc;
return ret;
}
static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x)
{
int ret;
ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0,
BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE);
if (ret)
return ret;
return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
BQ2515X_HWRESET_14S_WD, 0);
}
static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x)
{
int ret;
int vbat_msb;
int vbat_lsb;
uint32_t vbat_measurement;
if (!bq2515x->mains_online)
bq2515x_wake_up(bq2515x);
ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb);
if (ret)
return ret;
ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb);
if (ret)
return ret;
vbat_measurement = (vbat_msb << 8) | vbat_lsb;
return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) *
BQ2515X_VBAT_MULTIPLIER;
}
static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x)
{
int ret;
int ichg_msb;
int ichg_lsb;
uint32_t ichg_measurement;
u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
unsigned int ichg_reg_code, reg_code;
unsigned int icharge_range = 0, pchrgctrl;
unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV;
if (!bq2515x->mains_online)
return -ENODATA;
ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb);
if (ret)
return ret;
ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb);
if (ret)
return ret;
ichg_measurement = (ichg_msb << 8) | ichg_lsb;
ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo);
if (ret)
return ret;
vlowv_sel = buvlo & BQ2515X_VLOWV_SEL;
if (vlowv_sel)
vlowv = BQ2515X_VLOWV_SEL_1B1_UV;
if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) {
ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL,
&pchrgctrl);
if (ret)
return ret;
reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
} else {
ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL,
&ichg_reg_code);
if (ret)
return ret;
reg_code = ichg_reg_code;
}
ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
if (ret)
return ret;
icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
if (icharge_range)
ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
return reg_code * (ichg_multiplier * ichg_measurement /
BQ2515X_ICHG_DIVISOR);
}
static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
{
int ret;
int ce_pin;
int icctrl2;
int charger_disable;
ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio);
ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2);
if (ret)
return ret;
charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE;
if (charger_disable || ce_pin)
return true;
return false;
}
static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val)
{
gpiod_set_value_cansleep(bq2515x->ce_gpio, val);
return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
BQ2515X_CHARGER_DISABLE, val);
}
static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x)
{
int ret;
u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
unsigned int ichg_reg_code;
unsigned int pchrgctrl;
unsigned int icharge_range;
ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code);
if (ret)
return ret;
ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
if (ret)
return ret;
icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
if (icharge_range)
ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
return ichg_reg_code * ichg_multiplier;
}
static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x,
int val)
{
int ret;
unsigned int ichg_reg_code;
u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
unsigned int icharge_range = 0;
if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA)
return -EINVAL;
if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) {
ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
icharge_range = BQ2515X_ICHARGE_RANGE;
}
bq2515x_set_charge_disable(bq2515x, 1);
ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
BQ2515X_ICHARGE_RANGE, icharge_range);
if (ret)
return ret;
ichg_reg_code = val / ichg_multiplier;
ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code);
if (ret)
return ret;
return bq2515x_set_charge_disable(bq2515x, 0);
}
static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x)
{
int ret;
unsigned int pchrgctrl;
unsigned int icharge_range;
u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
unsigned int precharge_reg_code;
ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
if (ret)
return ret;
icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
if (icharge_range)
precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
return precharge_reg_code * precharge_multiplier;
}
static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x,
int val)
{
int ret;
unsigned int pchrgctrl;
unsigned int icharge_range;
unsigned int precharge_reg_code;
unsigned int precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
unsigned int precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA;
ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
if (ret)
return ret;
icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
if (icharge_range) {
precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA;
precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
} else {
precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA;
precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
}
if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA)
return -EINVAL;
precharge_reg_code = val / precharge_multiplier;
ret = bq2515x_set_charge_disable(bq2515x, 1);
if (ret)
return ret;
ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
BQ2515X_PRECHARGE_MASK, precharge_reg_code);
if (ret)
return ret;
return bq2515x_set_charge_disable(bq2515x, 0);
}
static int bq2515x_charging_status(struct bq2515x_device *bq2515x,
union power_supply_propval *val)
{
bool status0_no_fault;
bool status1_no_fault;
bool ce_status;
bool charge_done;
unsigned int status;
int ret;
if (!bq2515x->mains_online) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
return 0;
}
ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status);
if (ret)
return ret;
/*
* The code block below is used to determine if any faults from the
* STAT0 register are disbaling charging or if the charge has completed
* according to the CHARGE_DONE_STAT bit.
*/
if (((status & BQ2515X_STAT0_MASK) == true) &
((status & BQ2515X_CHRG_DONE) == false)) {
status0_no_fault = true;
charge_done = false;
} else if (status & BQ2515X_CHRG_DONE) {
charge_done = true;
status0_no_fault = false;
} else {
status0_no_fault = false;
charge_done = false;
}
ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status);
if (ret)
return ret;
/*
* The code block below is used to determine if any faults from the
* STAT1 register are disbaling charging
*/
if ((status & BQ2515X_STAT1_MASK) == false)
status1_no_fault = true;
else
status1_no_fault = false;
ce_status = (!bq2515x_get_charge_disable(bq2515x));
/*
* If there are no faults and charging is enabled, then status is
* charging. Otherwise, if charging is complete, then status is full.
* Otherwise, if a fault exists or charging is disabled, then status is
* not charging
*/
if (status0_no_fault & status1_no_fault & ce_status)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (charge_done)
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (!(status0_no_fault & status1_no_fault & ce_status))
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
}
static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x)
{
int vbat_reg_code;
int ret;
ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code);
if (ret)
return ret;
return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV;
}
static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val)
{
int vbat_reg_code;
if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN)
return -EINVAL;
vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV;
return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code);
}
static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x)
{
int ret;
int ilimctrl;
ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl);
if (ret)
return ret;
return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK];
}
static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val)
{
int i = 0;
unsigned int array_size = ARRAY_SIZE(bq2515x_ilim_lvl_values);
for (i = array_size - 1; i > 0; i--) {
if (val >= bq2515x_ilim_lvl_values[i])
break;
}
return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i);
}
static int bq2515x_power_supply_property_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
switch (prop) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
return true;
default:
return false;
}
}
static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x,
union power_supply_propval *val)
{
int health = POWER_SUPPLY_HEALTH_GOOD;
int ret;
unsigned int stat1;
unsigned int flag3;
if (!bq2515x->mains_online)
bq2515x_wake_up(bq2515x);
ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3);
if (ret)
return ret;
ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1);
if (ret)
return ret;
if (stat1 & BQ2515X_HEALTH_MASK) {
switch (stat1 & BQ2515X_HEALTH_MASK) {
case BQ2515X_TS_HOT_STAT:
health = POWER_SUPPLY_HEALTH_HOT;
break;
case BQ2515X_TS_WARM_STAT:
health = POWER_SUPPLY_HEALTH_WARM;
break;
case BQ2515X_TS_COOL_STAT:
health = POWER_SUPPLY_HEALTH_COOL;
break;
case BQ2515X_TS_COLD_STAT:
health = POWER_SUPPLY_HEALTH_COLD;
break;
default:
health = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
}
}
if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT)
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
if (flag3 & BQ2515X_SAFETY_TIMER_EXP)
health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
val->intval = health;
return 0;
}
static int bq2515x_mains_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
int ret;
switch (prop) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = bq2515x_set_batt_reg(bq2515x, val->intval);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = bq2515x_set_const_charge_current(bq2515x, val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq2515x_set_ilim_lvl(bq2515x, val->intval);
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
ret = bq2515x_set_precharge_current(bq2515x, val->intval);
break;
default:
return -EINVAL;
}
return ret;
}
static int bq2515x_mains_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
int ret = 0;
switch (prop) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = bq2515x_get_const_charge_current(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = bq2515x_get_batt_reg(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
ret = bq2515x_get_precharge_current(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = bq2515x->mains_online;
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = bq2515x_charger_get_health(bq2515x, val);
if (ret)
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq2515x_get_ilim_lvl(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = bq2515x->model_name;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = BQ2515X_MANUFACTURER;
break;
case POWER_SUPPLY_PROP_STATUS:
ret = bq2515x_charging_status(bq2515x, val);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
return ret;
}
static int bq2515x_battery_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
int ret;
ret = bq2515x_update_ps_status(bq2515x);
if (ret)
return ret;
switch (prop) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
ret = bq2515x->init_data.vbatreg;
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
ret = bq2515x->init_data.ichg;
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bq2515x_get_battery_voltage_now(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = bq2515x_get_battery_current_now(bq2515x);
if (ret < 0)
return ret;
val->intval = ret;
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property bq2515x_battery_properties[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
};
static enum power_supply_property bq2515x_mains_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
};
static struct power_supply_desc bq2515x_mains_desc = {
.name = "bq2515x-mains",
.type = POWER_SUPPLY_TYPE_MAINS,
.get_property = bq2515x_mains_get_property,
.set_property = bq2515x_mains_set_property,
.properties = bq2515x_mains_properties,
.num_properties = ARRAY_SIZE(bq2515x_mains_properties),
.property_is_writeable = bq2515x_power_supply_property_is_writeable,
};
static struct power_supply_desc bq2515x_battery_desc = {
.name = "bq2515x-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = bq2515x_battery_get_property,
.properties = bq2515x_battery_properties,
.num_properties = ARRAY_SIZE(bq2515x_battery_properties),
.property_is_writeable = bq2515x_power_supply_property_is_writeable,
};
static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x,
struct device *dev, struct power_supply_config psy_cfg)
{
bq2515x->mains = devm_power_supply_register(bq2515x->dev,
&bq2515x_mains_desc,
&psy_cfg);
if (IS_ERR(bq2515x->mains))
return -EINVAL;
bq2515x->battery = devm_power_supply_register(bq2515x->dev,
&bq2515x_battery_desc,
&psy_cfg);
if (IS_ERR(bq2515x->battery))
return -EINVAL;
return 0;
}
static int bq2515x_hw_init(struct bq2515x_device *bq2515x)
{
int ret;
struct power_supply_battery_info bat_info = { };
ret = bq2515x_disable_watchdog_timers(bq2515x);
if (ret)
return ret;
if (bq2515x->init_data.ilim) {
ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim);
if (ret)
return ret;
}
ret = power_supply_get_battery_info(bq2515x->mains, &bat_info);
if (ret) {
dev_warn(bq2515x->dev, "battery info missing, default values will be applied\n");
bq2515x->init_data.ichg = BQ2515X_DEFAULT_ICHG_UA;
bq2515x->init_data.vbatreg = BQ2515X_DEFAULT_VBAT_REG_UV;
bq2515x->init_data.iprechg = BQ2515X_DEFAULT_IPRECHARGE_UA;
} else {
bq2515x->init_data.ichg =
bat_info.constant_charge_current_max_ua;
bq2515x->init_data.vbatreg =
bat_info.constant_charge_voltage_max_uv;
bq2515x->init_data.iprechg =
bat_info.precharge_current_ua;
}
ret = bq2515x_set_const_charge_current(bq2515x,
bq2515x->init_data.ichg);
if (ret)
return ret;
ret = bq2515x_set_batt_reg(bq2515x, bq2515x->init_data.vbatreg);
if (ret)
return ret;
return bq2515x_set_precharge_current(bq2515x,
bq2515x->init_data.iprechg);
}
static int bq2515x_read_properties(struct bq2515x_device *bq2515x)
{
int ret;
ret = device_property_read_u32(bq2515x->dev,
"input-current-limit-microamp",
&bq2515x->init_data.ilim);
if (ret) {
switch (bq2515x->device_id) {
case BQ25150:
bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA;
break;
case BQ25155:
bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA;
break;
}
}
bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev,
"ac-detect", GPIOD_IN);
if (IS_ERR(bq2515x->ac_detect_gpio)) {
ret = PTR_ERR(bq2515x->ac_detect_gpio);
dev_err(bq2515x->dev, "Failed to get ac detect");
return ret;
}
bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(bq2515x->reset_gpio)) {
ret = PTR_ERR(bq2515x->reset_gpio);
dev_err(bq2515x->dev, "Failed to get reset");
return ret;
}
bq2515x->powerdown_gpio = devm_gpiod_get_optional(bq2515x->dev,
"powerdown", GPIOD_OUT_LOW);
if (IS_ERR(bq2515x->powerdown_gpio)) {
ret = PTR_ERR(bq2515x->powerdown_gpio);
dev_err(bq2515x->dev, "Failed to get powerdown");
return ret;
}
bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev,
"charge-enable",
GPIOD_OUT_LOW);
if (IS_ERR(bq2515x->ce_gpio)) {
ret = PTR_ERR(bq2515x->ce_gpio);
dev_err(bq2515x->dev, "Failed to get ce");
return ret;
}
return 0;
}
static bool bq2515x_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case BQ2515X_STAT0 ... BQ2515X_FLAG3:
case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L:
return true;
default:
return false;
}
}
static const struct regmap_config bq25150_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BQ2515X_DEVICE_ID,
.reg_defaults = bq25150_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(bq25150_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = bq2515x_volatile_register,
};
static const struct regmap_config bq25155_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BQ2515X_DEVICE_ID,
.reg_defaults = bq25155_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(bq25155_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = bq2515x_volatile_register,
};
static int bq2515x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct bq2515x_device *bq2515x;
struct power_supply_config charger_cfg = {};
int ret;
bq2515x = devm_kzalloc(dev, sizeof(*bq2515x), GFP_KERNEL);
if (!bq2515x)
return -ENOMEM;
bq2515x->dev = dev;
strncpy(bq2515x->model_name, id->name, I2C_NAME_SIZE);
bq2515x->device_id = id->driver_data;
switch (bq2515x->device_id) {
case BQ25150:
bq2515x->regmap = devm_regmap_init_i2c(client,
&bq25150_regmap_config);
break;
case BQ25155:
bq2515x->regmap = devm_regmap_init_i2c(client,
&bq25155_regmap_config);
break;
}
if (IS_ERR(bq2515x->regmap)) {
dev_err(dev, "failed to allocate register map\n");
return PTR_ERR(bq2515x->regmap);
}
i2c_set_clientdata(client, bq2515x);
charger_cfg.drv_data = bq2515x;
charger_cfg.of_node = dev->of_node;
ret = bq2515x_read_properties(bq2515x);
if (ret) {
dev_err(dev, "Failed to read device tree properties %d\n",
ret);
return ret;
}
ret = bq2515x_power_supply_register(bq2515x, dev, charger_cfg);
if (ret) {
dev_err(dev, "failed to register power supply\n");
return ret;
}
ret = bq2515x_hw_init(bq2515x);
if (ret) {
dev_err(dev, "Cannot initialize the chip\n");
return ret;
}
return 0;
}
static const struct i2c_device_id bq2515x_i2c_ids[] = {
{ "bq25150", BQ25150, },
{ "bq25155", BQ25155, },
{},
};
MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids);
static const struct of_device_id bq2515x_of_match[] = {
{ .compatible = "ti,bq25150", },
{ .compatible = "ti,bq25155", },
{ },
};
MODULE_DEVICE_TABLE(of, bq2515x_of_match);
static struct i2c_driver bq2515x_driver = {
.driver = {
.name = "bq2515x-charger",
.of_match_table = bq2515x_of_match,
},
.probe = bq2515x_probe,
.id_table = bq2515x_i2c_ids,
};
module_i2c_driver(bq2515x_driver);
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
MODULE_DESCRIPTION("BQ2515X charger driver");
MODULE_LICENSE("GPL v2");
......@@ -18,31 +18,33 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Datasheets:
* http://www.ti.com/product/bq27000
* http://www.ti.com/product/bq27200
* http://www.ti.com/product/bq27010
* http://www.ti.com/product/bq27210
* http://www.ti.com/product/bq27500
* http://www.ti.com/product/bq27510-g1
* http://www.ti.com/product/bq27510-g2
* http://www.ti.com/product/bq27510-g3
* http://www.ti.com/product/bq27520-g1
* http://www.ti.com/product/bq27520-g2
* http://www.ti.com/product/bq27520-g3
* http://www.ti.com/product/bq27520-g4
* http://www.ti.com/product/bq27530-g1
* http://www.ti.com/product/bq27531-g1
* http://www.ti.com/product/bq27541-g1
* http://www.ti.com/product/bq27542-g1
* http://www.ti.com/product/bq27546-g1
* http://www.ti.com/product/bq27742-g1
* http://www.ti.com/product/bq27545-g1
* http://www.ti.com/product/bq27421-g1
* http://www.ti.com/product/bq27425-g1
* http://www.ti.com/product/bq27426
* http://www.ti.com/product/bq27411-g1
* http://www.ti.com/product/bq27441-g1
* http://www.ti.com/product/bq27621-g1
* https://www.ti.com/product/bq27000
* https://www.ti.com/product/bq27200
* https://www.ti.com/product/bq27010
* https://www.ti.com/product/bq27210
* https://www.ti.com/product/bq27500
* https://www.ti.com/product/bq27510-g1
* https://www.ti.com/product/bq27510-g2
* https://www.ti.com/product/bq27510-g3
* https://www.ti.com/product/bq27520-g1
* https://www.ti.com/product/bq27520-g2
* https://www.ti.com/product/bq27520-g3
* https://www.ti.com/product/bq27520-g4
* https://www.ti.com/product/bq27530-g1
* https://www.ti.com/product/bq27531-g1
* https://www.ti.com/product/bq27541-g1
* https://www.ti.com/product/bq27542-g1
* https://www.ti.com/product/bq27546-g1
* https://www.ti.com/product/bq27742-g1
* https://www.ti.com/product/bq27545-g1
* https://www.ti.com/product/bq27421-g1
* https://www.ti.com/product/bq27425-g1
* https://www.ti.com/product/bq27426
* https://www.ti.com/product/bq27411-g1
* https://www.ti.com/product/bq27441-g1
* https://www.ti.com/product/bq27621-g1
* https://www.ti.com/product/bq27z561
* https://www.ti.com/product/bq28z610
*/
#include <linux/device.h>
......@@ -79,6 +81,11 @@
#define BQ27000_FLAG_FC BIT(5)
#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
/* BQ27Z561 has different layout for Flags register */
#define BQ27Z561_FLAG_FDC BIT(4) /* Battery fully discharged */
#define BQ27Z561_FLAG_FC BIT(5) /* Battery fully charged */
#define BQ27Z561_FLAG_DIS_CH BIT(6) /* Battery is discharging */
/* control register params */
#define BQ27XXX_SEALED 0x20
#define BQ27XXX_SET_CFGUPDATE 0x13
......@@ -431,12 +438,52 @@ static u8
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x18,
BQ27XXX_DM_REG_ROWS,
};
},
#define bq27411_regs bq27421_regs
#define bq27425_regs bq27421_regs
#define bq27426_regs bq27421_regs
#define bq27441_regs bq27421_regs
#define bq27621_regs bq27421_regs
bq27z561_regs[BQ27XXX_REG_MAX] = {
[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] = INVALID_REG_ADDR,
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x22,
BQ27XXX_DM_REG_ROWS,
},
bq28z610_regs[BQ27XXX_REG_MAX] = {
[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] = INVALID_REG_ADDR,
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x22,
BQ27XXX_DM_REG_ROWS,
};
static enum power_supply_property bq27000_props[] = {
POWER_SUPPLY_PROP_STATUS,
......@@ -672,6 +719,44 @@ static enum power_supply_property bq27421_props[] = {
#define bq27441_props bq27421_props
#define bq27621_props bq27421_props
static enum power_supply_property bq27z561_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_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq28z610_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_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
struct bq27xxx_dm_reg {
u8 subclass_id;
u8 offset;
......@@ -767,11 +852,15 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
#define bq27621_dm_regs 0
#endif
#define bq27z561_dm_regs 0
#define bq28z610_dm_regs 0
#define BQ27XXX_O_ZERO 0x00000001
#define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */
#define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */
#define BQ27XXX_O_CFGUP 0x00000008
#define BQ27XXX_O_RAM 0x00000010
#define BQ27Z561_O_BITS 0x00000020
#define BQ27XXX_DATA(ref, key, opt) { \
.opts = (opt), \
......@@ -816,6 +905,8 @@ static struct {
[BQ27426] = BQ27XXX_DATA(bq27426, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
[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),
[BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS),
[BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS),
};
static DEFINE_MUTEX(bq27xxx_list_lock);
......@@ -1551,6 +1642,8 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
{
if (di->opts & BQ27XXX_O_ZERO)
return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF);
else if (di->opts & BQ27Z561_O_BITS)
return flags & BQ27Z561_FLAG_FDC;
else
return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
}
......@@ -1595,6 +1688,7 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
cache.charge_full = bq27xxx_battery_read_fcc(di);
cache.capacity = bq27xxx_battery_read_soc(di);
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
......@@ -1682,6 +1776,13 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
else
status = POWER_SUPPLY_STATUS_DISCHARGING;
} else if (di->opts & BQ27Z561_O_BITS) {
if (di->cache.flags & BQ27Z561_FLAG_FC)
status = POWER_SUPPLY_STATUS_FULL;
else if (di->cache.flags & BQ27Z561_FLAG_DIS_CH)
status = POWER_SUPPLY_STATUS_DISCHARGING;
else
status = POWER_SUPPLY_STATUS_CHARGING;
} else {
if (di->cache.flags & BQ27XXX_FLAG_FC)
status = POWER_SUPPLY_STATUS_FULL;
......@@ -1710,6 +1811,13 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
} else if (di->opts & BQ27Z561_O_BITS) {
if (di->cache.flags & BQ27Z561_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27Z561_FLAG_FDC)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
} else {
if (di->cache.flags & BQ27XXX_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
......
/*
* BQ27xxx battery monitor HDQ/1-wire driver
*
* Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/
* Copyright (C) 2007-2017 Texas Instruments Incorporated - https://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
......
/*
* BQ27xxx battery monitor I2C driver
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
* Andrew F. Davis <afd@ti.com>
*
* This program is free software; you can redistribute it and/or modify
......@@ -253,6 +253,8 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27426", BQ27426 },
{ "bq27441", BQ27441 },
{ "bq27621", BQ27621 },
{ "bq27z561", BQ27Z561 },
{ "bq28z610", BQ28Z610 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
......@@ -286,6 +288,8 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
{ .compatible = "ti,bq27426" },
{ .compatible = "ti,bq27441" },
{ .compatible = "ti,bq27621" },
{ .compatible = "ti,bq27z561" },
{ .compatible = "ti,bq28z610" },
{},
};
MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
......
......@@ -274,7 +274,7 @@ static int cpcap_battery_cc_to_ua(struct cpcap_battery_ddata *ddata,
/**
* cpcap_battery_read_accumulated - reads cpcap coulomb counter
* @ddata: device driver data
* @regs: coulomb counter values
* @ccd: coulomb counter values
*
* Based on Motorola mapphone kernel function data_read_regs().
* Looking at the registers, the coulomb counter seems similar to
......
......@@ -172,17 +172,7 @@ static int bat_debug_show(struct seq_file *s, void *data)
return 0;
}
static int debug_open(struct inode *inode, struct file *file)
{
return single_open(file, bat_debug_show, inode->i_private);
}
static const struct file_operations bat_debug_fops = {
.open = debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(bat_debug);
static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
{
......
......@@ -112,9 +112,14 @@ static int gpio_charger_get_irq(struct device *dev, void *dev_id,
return irq;
}
/*
* The entries will be overwritten by driver's probe routine depending
* on the available features. This list ensures, that the array is big
* enough for all optional features.
*/
static enum power_supply_property gpio_charger_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS /* Must always be last in the array. */
POWER_SUPPLY_PROP_STATUS,
};
static int gpio_charger_probe(struct platform_device *pdev)
......@@ -128,6 +133,7 @@ static int gpio_charger_probe(struct platform_device *pdev)
int charge_status_irq;
unsigned long flags;
int ret;
int num_props = 0;
if (!pdata && !dev->of_node) {
dev_err(dev, "No platform data\n");
......@@ -142,13 +148,13 @@ static int gpio_charger_probe(struct platform_device *pdev)
* This will fetch a GPIO descriptor from device tree, ACPI or
* boardfile descriptor tables. It's good to try this first.
*/
gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
gpio_charger->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
/*
* If this fails and we're not using device tree, try the
* legacy platform data method.
* Fallback to legacy platform data method, if no GPIO is specified
* using boardfile descriptor tables.
*/
if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
if (!gpio_charger->gpiod && pdata) {
/* Non-DT: use legacy GPIO numbers */
if (!gpio_is_valid(pdata->gpio)) {
dev_err(dev, "Invalid gpio pin in pdata\n");
......@@ -173,17 +179,23 @@ static int gpio_charger_probe(struct platform_device *pdev)
return PTR_ERR(gpio_charger->gpiod);
}
if (gpio_charger->gpiod) {
gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_ONLINE;
num_props++;
}
charge_status = devm_gpiod_get_optional(dev, "charge-status", GPIOD_IN);
gpio_charger->charge_status = charge_status;
if (IS_ERR(gpio_charger->charge_status))
return PTR_ERR(gpio_charger->charge_status);
if (IS_ERR(charge_status))
return PTR_ERR(charge_status);
if (charge_status) {
gpio_charger->charge_status = charge_status;
gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_STATUS;
num_props++;
}
charger_desc = &gpio_charger->charger_desc;
charger_desc->properties = gpio_charger_properties;
charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
/* Remove POWER_SUPPLY_PROP_STATUS from the supported properties. */
if (!gpio_charger->charge_status)
charger_desc->num_properties -= 1;
charger_desc->num_properties = num_props;
charger_desc->get_property = gpio_charger_get_property;
psy_cfg.of_node = dev->of_node;
......@@ -269,6 +281,6 @@ static struct platform_driver gpio_charger_driver = {
module_platform_driver(gpio_charger_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
MODULE_DESCRIPTION("Driver for chargers only communicating via GPIO(s)");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-charger");
......@@ -69,6 +69,9 @@ static int max17040_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chip->soc;
break;
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
val->intval = chip->low_soc_alert;
break;
default:
return -EINVAL;
}
......@@ -256,19 +259,57 @@ static int max17040_enable_alert_irq(struct max17040_chip *chip)
return ret;
}
static int max17040_prop_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
return 1;
default:
return 0;
}
}
static int max17040_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct max17040_chip *chip = power_supply_get_drvdata(psy);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
/* alert threshold can be programmed from 1% up to 32% */
if ((val->intval < 1) || (val->intval > 32)) {
ret = -EINVAL;
break;
}
ret = max17040_set_low_soc_alert(chip->client, val->intval);
chip->low_soc_alert = val->intval;
break;
default:
ret = -EINVAL;
}
return ret;
}
static enum power_supply_property max17040_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
};
static const struct power_supply_desc max17040_battery_desc = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = max17040_get_property,
.properties = max17040_battery_props,
.num_properties = ARRAY_SIZE(max17040_battery_props),
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = max17040_get_property,
.set_property = max17040_set_property,
.property_is_writeable = max17040_prop_writeable,
.properties = max17040_battery_props,
.num_properties = ARRAY_SIZE(max17040_battery_props),
};
static int max17040_probe(struct i2c_client *client,
......
......@@ -23,6 +23,7 @@ struct max8998_battery_data {
static enum power_supply_property max8998_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
POWER_SUPPLY_PROP_STATUS, /* charger is charging/discharging/full */
};
/* Note that the charger control is done by a current regulator "CHARGER" */
......@@ -49,10 +50,28 @@ static int max8998_battery_get_property(struct power_supply *psy,
ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
if (ret)
return ret;
if (reg & (1 << 3))
val->intval = 0;
else
if (reg & (1 << 5))
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_STATUS:
ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
if (ret)
return ret;
if (!(reg & (1 << 5))) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
} else {
if (reg & (1 << 6))
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (reg & (1 << 3))
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
break;
default:
return -EINVAL;
......
......@@ -733,7 +733,7 @@ EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
* percent
* @table: Pointer to battery resistance temperature table
* @table_len: The table length
* @ocv: Current temperature
* @temp: Current temperature
*
* This helper function is used to look up battery internal resistance percent
* according to current temperature value from the resistance temperature table,
......
......@@ -87,6 +87,7 @@ static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = {
[POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard",
[POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive",
[POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom",
[POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life",
};
static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
......@@ -101,6 +102,9 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
[POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire",
[POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current",
[POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required",
[POWER_SUPPLY_HEALTH_WARM] = "Warm",
[POWER_SUPPLY_HEALTH_COOL] = "Cool",
[POWER_SUPPLY_HEALTH_HOT] = "Hot",
};
static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {
......@@ -343,7 +347,7 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
struct attribute *attr,
int attrno)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = dev_get_drvdata(dev);
umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
int i;
......
......@@ -125,7 +125,7 @@ static int rt5033_battery_probe(struct i2c_client *client,
battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
if (!battery)
return -EINVAL;
return -ENOMEM;
battery->client = client;
battery->regmap = devm_regmap_init_i2c(client,
......
......@@ -51,6 +51,14 @@ enum {
REG_CHARGE_VOLTAGE,
};
#define REG_ADDR_SPEC_INFO 0x1A
#define SPEC_INFO_VERSION_MASK GENMASK(7, 4)
#define SPEC_INFO_VERSION_SHIFT 4
#define SBS_VERSION_1_0 1
#define SBS_VERSION_1_1 2
#define SBS_VERSION_1_1_WITH_PEC 3
#define REG_ADDR_MANUFACTURE_DATE 0x1B
/* Battery Mode defines */
......@@ -224,14 +232,57 @@ static void sbs_disable_charger_broadcasts(struct sbs_info *chip)
static int sbs_update_presence(struct sbs_info *chip, bool is_present)
{
struct i2c_client *client = chip->client;
int retries = chip->i2c_retry_count;
s32 ret = 0;
u8 version;
if (chip->is_present == is_present)
return 0;
if (!is_present) {
chip->is_present = false;
/* Disable PEC when no device is present */
client->flags &= ~I2C_CLIENT_PEC;
return 0;
}
/* Check if device supports packet error checking and use it */
while (retries > 0) {
ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO);
if (ret >= 0)
break;
/*
* Some batteries trigger the detection pin before the
* I2C bus is properly connected. This works around the
* issue.
*/
msleep(100);
retries--;
}
if (ret < 0) {
dev_dbg(&client->dev, "failed to read spec info: %d\n", ret);
/* fallback to old behaviour */
client->flags &= ~I2C_CLIENT_PEC;
chip->is_present = true;
return ret;
}
version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT;
if (version == SBS_VERSION_1_1_WITH_PEC)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ?
"enabled" : "disabled");
if (!chip->is_present && is_present && !chip->charger_broadcasts)
sbs_disable_charger_broadcasts(chip);
......@@ -263,8 +314,7 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address)
return ret;
}
static int sbs_read_string_data(struct i2c_client *client, u8 address,
char *values)
static int sbs_read_string_data_fallback(struct i2c_client *client, u8 address, char *values)
{
struct sbs_info *chip = i2c_get_clientdata(client);
s32 ret = 0, block_length = 0;
......@@ -274,6 +324,9 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address,
retries_length = chip->i2c_retry_count;
retries_block = chip->i2c_retry_count;
dev_warn_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_BLOCK_DATA.\n"
"Fallback method does not support PEC.\n");
/* Adapter needs to support these two functions */
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
......@@ -329,6 +382,38 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address,
return ret;
}
static int sbs_read_string_data(struct i2c_client *client, u8 address, char *values)
{
struct sbs_info *chip = i2c_get_clientdata(client);
int retries = chip->i2c_retry_count;
int ret = 0;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
bool pec = client->flags & I2C_CLIENT_PEC;
client->flags &= ~I2C_CLIENT_PEC;
ret = sbs_read_string_data_fallback(client, address, values);
if (pec)
client->flags |= I2C_CLIENT_PEC;
return ret;
}
while (retries > 0) {
ret = i2c_smbus_read_block_data(client, address, values);
if (ret >= 0)
break;
retries--;
}
if (ret < 0) {
dev_dbg(&client->dev, "failed to read block 0x%x: %d\n", address, ret);
return ret;
}
/* add string termination */
values[ret] = '\0';
return ret;
}
static int sbs_write_word_data(struct i2c_client *client, u8 address,
u16 value)
{
......
......@@ -5,6 +5,7 @@
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
......@@ -133,14 +134,14 @@ static const char * const sc27xx_charger_supply_name[] = {
"sc2723_charger",
};
static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
}
static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
}
static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
......
......@@ -34,7 +34,7 @@ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION;
static int battery_capacity = 50;
static int battery_voltage = 3300;
static int battery_charge_counter = -1000;
static int battery_current = 1600;
static int battery_current = -1600;
static bool module_initialized;
......
......@@ -27,6 +27,7 @@ enum charge_mode {
CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */
CHARGE_MODE_AUTO = 4, /* Used for Adaptive */
CHARGE_MODE_CUSTOM = 5, /* Used for Custom */
CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */
};
#define CHARGE_LOWER_LIMIT_MIN 50
......@@ -48,6 +49,8 @@ static int psp_val_to_charge_mode(int psp_val)
return CHARGE_MODE_AUTO;
case POWER_SUPPLY_CHARGE_TYPE_CUSTOM:
return CHARGE_MODE_CUSTOM;
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
return CHARGE_MODE_LONGLIFE;
default:
return -EINVAL;
}
......@@ -67,6 +70,8 @@ static int charge_mode_to_psp_val(enum charge_mode mode)
return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
case CHARGE_MODE_CUSTOM:
return POWER_SUPPLY_CHARGE_TYPE_CUSTOM;
case CHARGE_MODE_LONGLIFE:
return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
default:
return -EINVAL;
}
......
......@@ -281,4 +281,23 @@ u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div);
#define DIV64_U64_ROUND_CLOSEST(dividend, divisor) \
({ u64 _tmp = (divisor); div64_u64((dividend) + _tmp / 2, _tmp); })
/*
* DIV_S64_ROUND_CLOSEST - signed 64bit divide with 32bit divisor rounded to nearest integer
* @dividend: signed 64bit dividend
* @divisor: signed 32bit divisor
*
* Divide signed 64bit dividend by signed 32bit divisor
* and round to closest integer.
*
* Return: dividend / divisor rounded to nearest integer
*/
#define DIV_S64_ROUND_CLOSEST(dividend, divisor)( \
{ \
s64 __x = (dividend); \
s32 __d = (divisor); \
((__x > 0) == (__d > 0)) ? \
div_s64((__x + (__d / 2)), __d) : \
div_s64((__x - (__d / 2)), __d); \
} \
)
#endif /* _LINUX_MATH64_H */
......@@ -14,8 +14,8 @@
* value is -1 then default chip value (specified in datasheet) will be
* used.
*
* Value resistor_sense is needed for for configuring charge and
* termination current. It it is less or equal to zero, configuring charge
* Value resistor_sense is needed for configuring charge and
* termination current. If it is less or equal to zero, configuring charge
* and termination current will not be possible.
*
* For automode support is needed to provide name of power supply device
......
......@@ -30,6 +30,8 @@ enum bq27xxx_chip {
BQ27426,
BQ27441,
BQ27621,
BQ27Z561,
BQ28Z610,
};
struct bq27xxx_device_info;
......
......@@ -48,6 +48,7 @@ enum {
POWER_SUPPLY_CHARGE_TYPE_STANDARD, /* normal speed */
POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE, /* dynamically adjusted speed */
POWER_SUPPLY_CHARGE_TYPE_CUSTOM, /* use CHARGE_CONTROL_* props */
POWER_SUPPLY_CHARGE_TYPE_LONGLIFE, /* slow speed, longer life */
};
enum {
......@@ -62,6 +63,9 @@ enum {
POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
POWER_SUPPLY_HEALTH_OVERCURRENT,
POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED,
POWER_SUPPLY_HEALTH_WARM,
POWER_SUPPLY_HEALTH_COOL,
POWER_SUPPLY_HEALTH_HOT,
};
enum {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment