power supply and reset changes for the v5.8 series

kobject:
  * Increase number of allowed uevent variables
 
 power-supply core:
  * Add power-supply type in uevent
  * Cleanup property handling in core
  * Make property and usb_type pointers const
  * Convert core power-supply DT binding to YAML
  * Cleanup HWMON code
  * Add new health status "calibration required"
  * Add new properties for manufacture date and
    capacity error margin
 
 battery drivers:
  * new cw2015 battery driver used by pine64 Pinebook Pro laptop
  * axp22: blacklist on Meegopad T02
  * sc27xx: support current/voltage reading
  * max17042: support time-to-empty reading
  * simple-battery: add more battery parameters
  * bq27xxx: convert DT binding document to YAML
  * sbs-battery: add TI BQ20Z65 support, fix technology property, convert
                 DT binding to YAML, add option to disable charger
 		broadcasts, add new properties: manufacture date,
 		capacity error margin, average current, charge current
 		and voltage and support calibration required health
 		status
  * misc. fixes
 
 charger drivers:
  * bq25890: cleanup, implement charge type, precharge current and input
             current limiting properties
  * bd70528: use new linear range helper library
  * bd99954: new charger driver
  * mp2629: new charger driver
  * misc. fixes
 
 reboot drivers:
  * oxnas-restart: introduce new driver
  * syscon-reboot: convert DT binding to YAML, add parent syscon device support
  * misc. fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAl7a2L0ACgkQ2O7X88g7
 +poAIg//caN2sHqOFEsukXWe1oft/X0IQeGNmlCnj55l1zGJ/x03Yn0GeKb0FZgM
 go+sfMaM/a6NNdmKhseLIsYFlhVBa2E7qW1nvaxgt66JKS+GXAHfE8LWVwazDJex
 rq0esXuegsqFdbCUth81YLk2H+0qwnhVVv/Urvv1RaE/woeFHAHL1cYfcFa+YDXm
 XRDT0W73YozslFkMnZMLBLyQzad3yVnNcnRYF3Dx0CMnUsjGfPjBlyk4RLPTcgUk
 8ChTvCcHRG7IhEtF0a1HUr3UjCy9rjwiqWIobQltnbEYImxY0LWkCVVr1EsNjeyr
 ikl3c4JfwmdlVCBCSPn294mPlGKu8DNBMLN1IgAuJHKW1GuQxd0Tcbd9OwF6VlVj
 WTFWp2GcoIjKQtOWKDeCqby+hoWhgclxTHUudo7FGTT0xBP9OWTKWDpDL18cZWd8
 dNCFMEI6MDMRtwL8+3ilLcnvSoMzyZ94TZmwii9toD3xSI7TMn7oVRuOlq1TkOEw
 ZuPS3QP6jBHm8NN9JEhLbrwradP+R8qpE/jpsnWiBDR6Jt7xx4W7W0xupq4GdHAl
 u0Lbh38/0bkTijeo9xoPe03KsSBV8HOr9Lf/QVW3zLcYcGKW17j8QTkes3a0bOs0
 DZHVCFeXuWNFa/tJxe6c+J+NsroLucTMwA3lBmlZhI/TPsG2vb8=
 =He9L
 -----END PGP SIGNATURE-----

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

Pull power supply and reset updates from Sebastian Reichel:
 "This time there are lots of changes. Quite a few changes to the core,
  lots of driver changes and one change to kobject core (with Ack from
  Greg).

  Summary:

  kobject:
   - Increase number of allowed uevent variables

  power-supply core:
   - Add power-supply type in uevent
   - Cleanup property handling in core
   - Make property and usb_type pointers const
   - Convert core power-supply DT binding to YAML
   - Cleanup HWMON code
   - Add new health status "calibration required"
   - Add new properties for manufacture date and capacity error margin

  battery drivers:
   - new cw2015 battery driver used by pine64 Pinebook Pro laptop
   - axp22: blacklist on Meegopad T02
   - sc27xx: support current/voltage reading
   - max17042: support time-to-empty reading
   - simple-battery: add more battery parameters
   - bq27xxx: convert DT binding document to YAML
   - sbs-battery: add TI BQ20Z65 support, fix technology property,
         convert DT binding to YAML, add option to disable charger
         broadcasts, add new properties: manufacture date, capacity
         error margin, average current, charge current and voltage and
         support calibration required health status
   - misc fixes

  charger drivers:
   - bq25890: cleanup, implement charge type, precharge current and
         input current limiting properties
   - bd70528: use new linear range helper library
   - bd99954: new charger driver
   - mp2629: new charger driver
   - misc fixes

  reboot drivers:
   - oxnas-restart: introduce new driver
   - syscon-reboot: convert DT binding to YAML, add parent syscon device
         support
   - misc fixes"

* tag 'for-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (85 commits)
  power: supply: cw2015: Attach OF ID table to the driver
  power: reset: gpio-poweroff: add missing '\n' in dev_err()
  Revert "power: supply: sbs-battery: simplify read_read_string_data"
  Revert "power: supply: sbs-battery: add PEC support"
  dt-bindings: power: sbs-battery: Convert to yaml
  power: supply: sbs-battery: constify power-supply property array
  power: supply: sbs-battery: switch to i2c's probe_new
  power: supply: sbs-battery: switch from of_property_* to device_property_*
  power: supply: sbs-battery: add ability to disable charger broadcasts
  power: supply: sbs-battery: fix idle battery status
  power: supply: sbs-battery: add POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED support
  power: supply: sbs-battery: add MANUFACTURE_DATE support
  power: supply: sbs-battery: add POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT/VOLTAGE_MAX support
  power: supply: sbs-battery: Improve POWER_SUPPLY_PROP_TECHNOLOGY support
  power: supply: sbs-battery: add POWER_SUPPLY_PROP_CURRENT_AVG support
  power: supply: sbs-battery: add PEC support
  power: supply: sbs-battery: simplify read_read_string_data
  power: supply: sbs-battery: add POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN support
  power: supply: sbs-battery: Add TI BQ20Z65 support
  power: supply: core: add POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED
  ...
This commit is contained in:
Linus Torvalds 2020-06-10 11:28:35 -07:00
commit 3a2a875174
53 changed files with 4691 additions and 676 deletions

View File

@ -74,6 +74,21 @@ Description:
Access: Read, Write
Valid values: 0 - 100 (percent)
What: /sys/class/power_supply/<supply_name>/capacity_error_margin
Date: April 2019
Contact: linux-pm@vger.kernel.org
Description:
Battery capacity measurement becomes unreliable without
recalibration. This values provides the maximum error
margin expected to exist by the fuel gauge in percent.
Values close to 0% will be returned after (re-)calibration
has happened. Over time the error margin will increase.
100% means, that the capacity related values are basically
completely useless.
Access: Read
Valid values: 0 - 100 (percent)
What: /sys/class/power_supply/<supply_name>/capacity_level
Date: June 2009
Contact: linux-pm@vger.kernel.org
@ -190,7 +205,7 @@ Description:
Valid values: "Unknown", "Good", "Overheat", "Dead",
"Over voltage", "Unspecified failure", "Cold",
"Watchdog timer expire", "Safety timer expire",
"Over current"
"Over current", "Calibration required"
What: /sys/class/power_supply/<supply_name>/precharge_current
Date: June 2017
@ -665,3 +680,31 @@ Description:
Valid values:
- 1: enabled
- 0: disabled
What: /sys/class/power_supply/<supply_name>/manufacture_year
Date: January 2020
Contact: linux-pm@vger.kernel.org
Description:
Reports the year (following Gregorian calendar) when the device has been
manufactured.
Access: Read
Valid values: Reported as integer
What: /sys/class/power_supply/<supply_name>/manufacture_month
Date: January 2020
Contact: linux-pm@vger.kernel.org
Description:
Reports the month when the device has been manufactured.
Access: Read
Valid values: 1-12
What: /sys/class/power_supply/<supply_name>/manufacture_day
Date: January 2020
Contact: linux-pm@vger.kernel.org
Description:
Reports the day of month when the device has been manufactured.
Access: Read
Valid values: 1-31

View File

@ -1,35 +0,0 @@
SYSCON reboot mode driver
This driver gets reboot mode magic value form reboot-mode driver
and stores it in a SYSCON mapped register. Then the bootloader
can read it and take different action according to the magic
value stored.
This DT node should be represented as a sub-node of a "syscon", "simple-mfd"
node.
Required properties:
- compatible: should be "syscon-reboot-mode"
- offset: offset in the register map for the storage register (in bytes)
Optional property:
- mask: bits mask of the bits in the register to store the reboot mode magic value,
default set to 0xffffffff if missing.
The rest of the properties should follow the generic reboot-mode description
found in reboot-mode.txt
Example:
pmu: pmu@20004000 {
compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd";
reg = <0x20004000 0x100>;
reboot-mode {
compatible = "syscon-reboot-mode";
offset = <0x40>;
mode-normal = <BOOT_NORMAL>;
mode-recovery = <BOOT_RECOVERY>;
mode-bootloader = <BOOT_FASTBOOT>;
mode-loader = <BOOT_BL_DOWNLOAD>;
};
};

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/reset/syscon-reboot-mode.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic SYSCON reboot mode driver
maintainers:
- Sebastian Reichel <sre@kernel.org>
description: |
This driver gets reboot mode magic value from reboot-mode driver
and stores it in a SYSCON mapped register. Then the bootloader
can read it and take different action according to the magic
value stored. The SYSCON mapped register is retrieved from the
parental dt-node plus the offset. So the SYSCON reboot-mode node
should be represented as a sub-node of a "syscon", "simple-mfd" node.
properties:
compatible:
const: syscon-reboot-mode
mask:
$ref: /schemas/types.yaml#/definitions/uint32
description: Update only the register bits defined by the mask (32 bit)
offset:
$ref: /schemas/types.yaml#/definitions/uint32
description: Offset in the register map for the mode register (in bytes)
patternProperties:
"^mode-.+":
$ref: /schemas/types.yaml#/definitions/uint32
description: Vendor-specific mode value written to the mode register
additionalProperties: false
required:
- compatible
- offset
examples:
- |
#include <dt-bindings/soc/rockchip,boot-mode.h>
reboot-mode {
compatible = "syscon-reboot-mode";
offset = <0x40>;
mode-normal = <BOOT_NORMAL>;
mode-recovery = <BOOT_RECOVERY>;
mode-bootloader = <BOOT_FASTBOOT>;
mode-loader = <BOOT_BL_DOWNLOAD>;
};
...

View File

@ -12,9 +12,12 @@ maintainers:
description: |+
This is a generic reset driver using syscon to map the reset register.
The reset is generally performed with a write to the reset register
defined by the register map pointed by syscon reference plus the offset
with the value and mask defined in the reboot node.
Default will be little endian mode, 32 bit access only.
defined by the SYSCON register map base plus the offset with the value and
mask defined in the reboot node. Default will be little endian mode, 32 bit
access only. The SYSCON registers map is normally retrieved from the
parental dt-node. So the SYSCON reboot node should be represented as a
sub-node of a "syscon", "simple-mfd" node. Though the regmap property
pointing to the system controller node is also supported.
properties:
compatible:
@ -30,7 +33,10 @@ properties:
regmap:
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle to the register map node.
deprecated: true
description: |
Phandle to the register map node. This property is deprecated in favor of
the syscon-reboot node been a child of a system controller node.
value:
$ref: /schemas/types.yaml#/definitions/uint32
@ -38,7 +44,6 @@ properties:
required:
- compatible
- regmap
- offset
additionalProperties: false

View File

@ -11,15 +11,21 @@ different type. This prevents unpredictable, potentially harmful,
behavior should a replacement that changes the battery type occur
without a corresponding update to the dtb.
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

View File

@ -1,56 +0,0 @@
TI BQ27XXX fuel gauge family
Required properties:
- compatible: contains one of the following:
* "ti,bq27200" - BQ27200
* "ti,bq27210" - BQ27210
* "ti,bq27500" - deprecated, use revision specific property below
* "ti,bq27510" - deprecated, use revision specific property below
* "ti,bq27520" - deprecated, use revision specific property below
* "ti,bq27500-1" - BQ27500/1
* "ti,bq27510g1" - BQ27510-g1
* "ti,bq27510g2" - BQ27510-g2
* "ti,bq27510g3" - BQ27510-g3
* "ti,bq27520g1" - BQ27520-g1
* "ti,bq27520g2" - BQ27520-g2
* "ti,bq27520g3" - BQ27520-g3
* "ti,bq27520g4" - BQ27520-g4
* "ti,bq27521" - BQ27521
* "ti,bq27530" - BQ27530
* "ti,bq27531" - BQ27531
* "ti,bq27541" - BQ27541
* "ti,bq27542" - BQ27542
* "ti,bq27546" - BQ27546
* "ti,bq27742" - BQ27742
* "ti,bq27545" - BQ27545
* "ti,bq27411" - BQ27411
* "ti,bq27421" - BQ27421
* "ti,bq27425" - BQ27425
* "ti,bq27426" - BQ27426
* "ti,bq27441" - BQ27441
* "ti,bq27621" - BQ27621
- reg: integer, I2C address of the fuel gauge.
Optional properties:
- monitored-battery: phandle of battery characteristics node
The fuel gauge uses the following battery properties:
+ energy-full-design-microwatt-hours
+ charge-full-design-microamp-hours
+ voltage-min-design-microvolt
Both or neither of the *-full-design-*-hours properties must be set.
See Documentation/devicetree/bindings/power/supply/battery.txt
Example:
bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
};
bq27510g3: fuel-gauge@55 {
compatible = "ti,bq27510g3";
reg = <0x55>;
monitored-battery = <&bat>;
};

View File

@ -0,0 +1,91 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020 Texas Instruments Incorporated
%YAML 1.2
---
$id: "http://devicetree.org/schemas/power/supply/bq27xxx.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: TI BQ27XXX fuel gauge family
maintainers:
- Pali Rohár <pali@kernel.org>
- Andrew F. Davis <afd@ti.com>
- Sebastian Reichel <sre@kernel.org>
description: |
Support various Texas Instruments fuel gauge devices that share similar
register maps and power supply properties
allOf:
- $ref: power-supply.yaml#
properties:
compatible:
enum:
- ti,bq27200
- ti,bq27210
- ti,bq27500 # deprecated, use revision specific property below
- ti,bq27510 # deprecated, use revision specific property below
- ti,bq27520 # deprecated, use revision specific property below
- ti,bq27500-1
- ti,bq27510g1
- ti,bq27510g2
- ti,bq27510g3
- ti,bq27520g1
- ti,bq27520g2
- ti,bq27520g3
- ti,bq27520g4
- ti,bq27521
- ti,bq27530
- ti,bq27531
- ti,bq27541
- ti,bq27542
- ti,bq27546
- ti,bq27742
- ti,bq27545
- ti,bq27411
- ti,bq27421
- ti,bq27425
- ti,bq27426
- ti,bq27441
- ti,bq27621
reg:
maxItems: 1
description: integer, I2C address of the fuel gauge.
monitored-battery:
description: |
phandle of battery characteristics node.
The fuel gauge uses the following battery properties:
- energy-full-design-microwatt-hours
- charge-full-design-microamp-hours
- voltage-min-design-microvolt
Both or neither of the *-full-design-*-hours properties must be set.
See Documentation/devicetree/bindings/power/supply/battery.txt
power-supplies: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
};
bq27510g3: fuel-gauge@55 {
compatible = "ti,bq27510g3";
reg = <0x55>;
monitored-battery = <&bat>;
};
};

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/cw2015_battery.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Battery driver for CW2015 shuntless fuel gauge by CellWise.
maintainers:
- Tobias Schramm <t.schramm@manjaro.org>
description: |
The driver can utilize information from a simple-battery linked via a
phandle in monitored-battery. If specified the driver uses the
charge-full-design-microamp-hours property of the battery.
properties:
compatible:
const: cellwise,cw2015
reg:
maxItems: 1
cellwise,battery-profile:
description: |
This property specifies characteristics of the battery used. The format
of this binary blob is kept secret by CellWise. The only way to obtain
it is to mail two batteries to a test facility of CellWise and receive
back a test report with the binary blob.
allOf:
- $ref: /schemas/types.yaml#definitions/uint8-array
items:
- minItems: 64
maxItems: 64
cellwise,monitor-interval-ms:
description:
Specifies the interval in milliseconds gauge values are polled at
minimum: 250
power-supplies:
description:
Specifies supplies used for charging the battery connected to this gauge
allOf:
- $ref: /schemas/types.yaml#/definitions/phandle-array
- minItems: 1
maxItems: 8 # Should be enough
monitored-battery:
description:
Specifies the phandle of a simple-battery connected to this gauge
$ref: /schemas/types.yaml#/definitions/phandle
required:
- compatible
- reg
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
cw2015@62 {
compatible = "cellwise,cw201x";
reg = <0x62>;
cellwise,battery-profile = /bits/ 8 <
0x17 0x67 0x80 0x73 0x6E 0x6C 0x6B 0x63
0x77 0x51 0x5C 0x58 0x50 0x4C 0x48 0x36
0x15 0x0C 0x0C 0x19 0x5B 0x7D 0x6F 0x69
0x69 0x5B 0x0C 0x29 0x20 0x40 0x52 0x59
0x57 0x56 0x54 0x4F 0x3B 0x1F 0x7F 0x17
0x06 0x1A 0x30 0x5A 0x85 0x93 0x96 0x2D
0x48 0x77 0x9C 0xB3 0x80 0x52 0x94 0xCB
0x2F 0x00 0x64 0xA5 0xB5 0x11 0xF0 0x11
>;
cellwise,monitor-interval-ms = <5000>;
monitored-battery = <&bat>;
power-supplies = <&mains_charger>, <&usb_charger>;
};
};

View File

@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: "http://devicetree.org/schemas/power/supply/power-supply.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Power Supply Core Support
maintainers:
- Sebastian Reichel <sre@kernel.org>
properties:
power-supplies:
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
This property is added to a supply in order to list the devices which
supply it power, referenced by their phandles.
examples:
- |
power {
#address-cells = <1>;
#size-cells = <0>;
usb_charger:charger@e {
compatible = "some,usb-charger";
reg = <0xe>;
};
ac_charger:charger@c {
compatible = "some,ac-charger";
reg = <0xc>;
};
battery:battery@b {
compatible = "some,battery";
reg = <0xb>;
power-supplies = <&usb_charger>, <&ac_charger>;
};
};

View File

@ -1,23 +1,2 @@
Power Supply Core Support
Optional Properties:
- power-supplies : This property is added to a supply in order to list the
devices which supply it power, referenced by their phandles.
Example:
usb-charger: power@e {
compatible = "some,usb-charger";
...
};
ac-charger: power@c {
compatible = "some,ac-charger";
...
};
battery@b {
compatible = "some,battery";
...
power-supplies = <&usb-charger>, <&ac-charger>;
};
This binding has been converted to yaml please see power-supply.yaml in this
directory.

View File

@ -0,0 +1,155 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/rohm,bd99954.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD99954 Battery charger
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
- Markus Laine <markus.laine@fi.rohmeurope.com>
- Mikko Mutanen <mikko.mutanen@fi.rohmeurope.com>
description: |
The ROHM BD99954 is a Battery Management LSI for 1-4 cell Lithium-Ion
secondary battery intended to be used in space-constraint equipment such
as Low profile Notebook PC, Tablets and other applications. BD99954
provides a Dual-source Battery Charger, two port BC1.2 detection and a
Battery Monitor.
properties:
compatible:
const: rohm,bd99954
#
# The battery charging profile of BD99954.
#
# Curve (1) represents charging current.
# Curve (2) represents battery voltage.
#
# The BD99954 data sheet divides charging to three phases.
# a) Trickle-charge with constant current (8).
# b) pre-charge with constant current (6)
# c) fast-charge with:
# First a constant current (5) phase (CC)
# Then constant voltage (CV) phase (after the battery voltage has reached
# target level - until charging current has dropped to termination
# level (7)
#
# V ^ ^ I
# . .
# . .
# (4)- -.- - - - - - - - - - - - - - +++++++++++++++++++++++++++.
# . / .
# . ++++++/++ - - - - - - - - - - - - -.- - (5)
# . + / + .
# . + - -- .
# . + - + .
# . +.- -: .
# . .+ +` .
# . .- + | `/ .
# . .." + .: .
# . -" + -- .
# . (2) ..." + | :- .
# . ..."" + -: .
# (3)- -.-.""- - - - -+++++++++ - - - - - - -.:- - - - - - - - - .- - (6)
# . + `:. .
# . + | -: .
# . + -: .
# . + .. .
# . (1) + | "+++- - - -.- - (7)
# -++++++++++++++- - - - - - - - - - - - - - - - - + - - - .- - (8)
# . + -
# -------------------------------------------------+++++++++-->
# | | | CC | CV |
# | --trickle-- | -pre- | ---------fast----------- |
#
# The charger uses the following battery properties
# - trickle-charge-current-microamp:
# Current used at trickle-charge phase (8 in above chart)
# minimum: 64000
# maximum: 1024000
# multipleOf: 64000
# - precharge-current-microamp:
# Current used at pre-charge phase (6 in above chart)
# minimum: 64000
# maximum: 1024000
# multipleOf: 64000
# - constant-charge-current-max-microamp
# Current used at fast charge constant current phase (5 in above chart)
# minimum: 64000
# maximum: 1024000
# multipleOf: 64000
# - constant-charge-voltage-max-microvolt
# The constant voltage used in fast charging phase (4 in above chart)
# minimum: 2560000
# maximum: 19200000
# multipleOf: 16000
# - precharge-upper-limit-microvolt
# charging mode is changed from trickle charging to pre-charging
# when battery voltage exceeds this limit voltage (3 in above chart)
# minimum: 2048000
# maximum: 19200000
# multipleOf: 64000
# - re-charge-voltage-microvolt
# minimum: 2560000
# maximum: 19200000
# multipleOf: 16000
# re-charging is automatically started when battry has been discharging
# to the point where the battery voltage drops below this limit
# - over-voltage-threshold-microvolt
# battery is expected to be faulty if battery voltage exceeds this limit.
# Charger will then enter to a "battery faulty" -state
# minimum: 2560000
# maximum: 19200000
# multipleOf: 16000
# - charge-term-current-microamp
# minimum: 0
# maximum: 1024000
# multipleOf: 64000
# a charge cycle terminates when the battery voltage is above recharge
# threshold, and the current is below this setting (7 in above chart)
# See also Documentation/devicetree/bindings/power/supply/battery.txt
monitored-battery:
description:
phandle of battery characteristics devicetree node
rohm,vsys-regulation-microvolt:
description: system specific lower limit for system voltage.
minimum: 2560000
maximum: 19200000
multipleOf: 64000
rohm,vbus-input-current-limit-microamp:
description: system specific VBUS input current limit (in microamps).
minimum: 32000
maximum: 16352000
multipleOf: 32000
rohm,vcc-input-current-limit-microamp:
description: system specific VCC/VACP input current limit (in microamps).
minimum: 32000
maximum: 16352000
multipleOf: 32000
required:
- compatible
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
charger@9 {
compatible = "rohm,bd99954";
monitored-battery = <&battery>;
reg = <0x9>;
interrupt-parent = <&gpio1>;
interrupts = <29 8>;
rohm,vsys-regulation-microvolt = <8960000>;
rohm,vbus-input-current-limit-microamp = <1472000>;
rohm,vcc-input-current-limit-microamp = <1472000>;
};
};

View File

@ -0,0 +1,83 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/sbs,sbs-battery.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: SBS compliant battery
maintainers:
- Sebastian Reichel <sre@kernel.org>
description: |
Battery compatible with the smart battery system specifications
properties:
compatible:
oneOf:
- items:
- enum:
- ti,bq20z65
- ti,bq20z75
- enum:
- sbs,sbs-battery
- items:
- const: sbs,sbs-battery
reg:
maxItems: 1
sbs,i2c-retry-count:
description:
The number of times to retry I2C transactions on I2C IO failure.
default: 0
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
sbs,poll-retry-count:
description:
The number of times to try looking for new status after an external
change notification.
default: 0
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
sbs,battery-detect-gpios:
description:
GPIO which signals battery detection. If this is not supplied, the bus
needs to be polled to detect the battery.
maxItems: 1
sbs,disable-charger-broadcasts:
description:
SBS batteries by default send broadcast messages to SBS compliant chargers to
configure max. charge current/voltage. If your hardware does not have an SBS
compliant charger it should be disabled via this property to avoid blocking
the bus. Also some SBS battery fuel gauges are known to have a buggy multi-
master implementation.
type: boolean
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
battery@b {
compatible = "ti,bq20z75", "sbs,sbs-battery";
reg = <0xb>;
sbs,i2c-retry-count = <2>;
sbs,poll-retry-count = <10>;
sbs,battery-detect-gpios = <&gpio 122 GPIO_ACTIVE_HIGH>;
sbs,disable-charger-broadcasts;
};
};

View File

@ -1,27 +0,0 @@
SBS sbs-battery
~~~~~~~~~~
Required properties :
- compatible: "<vendor>,<part-number>", "sbs,sbs-battery" as fallback. The
part number compatible string might be used in order to take care of
vendor specific registers.
Known <vendor>,<part-number>:
ti,bq20z75
Optional properties :
- sbs,i2c-retry-count : The number of times to retry i2c transactions on i2c
IO failure.
- sbs,poll-retry-count : The number of times to try looking for new status
after an external change notification.
- sbs,battery-detect-gpios : The gpio which signals battery detection and
a flag specifying its polarity.
Example:
battery@b {
compatible = "ti,bq20z75", "sbs,sbs-battery";
reg = <0xb>;
sbs,i2c-retry-count = <2>;
sbs,poll-retry-count = <10>;
sbs,battery-detect-gpios = <&gpio-controller 122 1>;
}

View File

@ -187,6 +187,8 @@ patternProperties:
description: Cadence Design Systems Inc.
"^cdtech,.*":
description: CDTech(H.K.) Electronics Limited
"^cellwise,.*":
description: CellWise Microelectronics Co., Ltd
"^ceva,.*":
description: Ceva, Inc.
"^checkpoint,.*":

View File

@ -2190,6 +2190,7 @@ L: linux-oxnas@groups.io (moderated for non-subscribers)
S: Maintained
F: arch/arm/boot/dts/ox8*.dts*
F: arch/arm/mach-oxnas/
F: drivers/power/reset/oxnas-restart.c
N: oxnas
ARM/PALM TREO SUPPORT
@ -3978,6 +3979,12 @@ F: arch/powerpc/include/uapi/asm/spu*.h
F: arch/powerpc/oprofile/*cell*
F: arch/powerpc/platforms/cell/
CELLWISE CW2015 BATTERY DRIVER
M: Tobias Schrammm <t.schramm@manjaro.org>
S: Maintained
F: Documentation/devicetree/bindings/power/supply/cw2015_battery.yaml
F: drivers/power/supply/cw2015_battery.c
CEPH COMMON CODE (LIBCEPH)
M: Ilya Dryomov <idryomov@gmail.com>
M: Jeff Layton <jlayton@kernel.org>

View File

@ -52,7 +52,7 @@ static const char * const lid_wake_mode_names[] = {
static void battery_status_changed(void)
{
struct power_supply *psy = power_supply_get_by_name("olpc-battery");
struct power_supply *psy = power_supply_get_by_name("olpc_battery");
if (psy) {
power_supply_changed(psy);
@ -62,7 +62,7 @@ static void battery_status_changed(void)
static void ac_status_changed(void)
{
struct power_supply *psy = power_supply_get_by_name("olpc-ac");
struct power_supply *psy = power_supply_get_by_name("olpc_ac");
if (psy) {
power_supply_changed(psy);

View File

@ -75,7 +75,7 @@ static struct kobj_attribute lid_wake_on_close_attr =
static void battery_status_changed(void)
{
struct power_supply *psy = power_supply_get_by_name("olpc-battery");
struct power_supply *psy = power_supply_get_by_name("olpc_battery");
if (psy) {
power_supply_changed(psy);
@ -85,7 +85,7 @@ static void battery_status_changed(void)
static void ac_status_changed(void)
{
struct power_supply *psy = power_supply_get_by_name("olpc-ac");
struct power_supply *psy = power_supply_get_by_name("olpc_ac");
if (psy) {
power_supply_changed(psy);

View File

@ -410,7 +410,7 @@ static void olpc_xo175_ec_complete(void *arg)
dev_dbg(dev, "got event %.2x\n", byte);
switch (byte) {
case EVENT_AC_CHANGE:
psy = power_supply_get_by_name("olpc-ac");
psy = power_supply_get_by_name("olpc_ac");
if (psy) {
power_supply_changed(psy);
power_supply_put(psy);
@ -420,7 +420,7 @@ static void olpc_xo175_ec_complete(void *arg)
case EVENT_BATTERY_CRITICAL:
case EVENT_BATTERY_SOC_CHANGE:
case EVENT_BATTERY_ERROR:
psy = power_supply_get_by_name("olpc-battery");
psy = power_supply_get_by_name("olpc_battery");
if (psy) {
power_supply_changed(psy);
power_supply_put(psy);

View File

@ -123,6 +123,13 @@ config POWER_RESET_OCELOT_RESET
help
This driver supports restart for Microsemi Ocelot SoC.
config POWER_RESET_OXNAS
bool "OXNAS SoC restart driver"
depends on ARCH_OXNAS
default MACH_OX820
help
Restart support for OXNAS/PLXTECH OX820 SoC.
config POWER_RESET_PIIX4_POWEROFF
tristate "Intel PIIX4 power-off driver"
depends on PCI

View File

@ -12,6 +12,7 @@ obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.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
obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o

View File

@ -54,7 +54,7 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
/* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
dev_err(&pdev->dev,
"%s: pm_power_off function already registered",
"%s: pm_power_off function already registered\n",
__func__);
return -EBUSY;
}

View File

@ -94,7 +94,6 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
{
ktime_t now;
int state;
unsigned long overruns;
struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde);
if (data->kernel_panic)
@ -104,7 +103,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
gpiod_set_value(data->gpio_watchdog, !state);
now = hrtimer_cb_get_time(timer);
overruns = hrtimer_forward(timer, now, data->wde_interval);
hrtimer_forward(timer, now, data->wde_interval);
return HRTIMER_RESTART;
}

View File

@ -0,0 +1,233 @@
// SPDX-License-Identifier: (GPL-2.0)
/*
* oxnas SoC reset driver
* based on:
* Microsemi MIPS SoC reset driver
* and ox820_assert_system_reset() written by Ma Hajun <mahaijuns@gmail.com>
*
* Copyright (c) 2013 Ma Hajun <mahaijuns@gmail.com>
* Copyright (c) 2017 Microsemi Corporation
* Copyright (c) 2020 Daniel Golle <daniel@makrotopia.org>
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/notifier.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
/* bit numbers of reset control register */
#define OX820_SYS_CTRL_RST_SCU 0
#define OX820_SYS_CTRL_RST_COPRO 1
#define OX820_SYS_CTRL_RST_ARM0 2
#define OX820_SYS_CTRL_RST_ARM1 3
#define OX820_SYS_CTRL_RST_USBHS 4
#define OX820_SYS_CTRL_RST_USBHSPHYA 5
#define OX820_SYS_CTRL_RST_MACA 6
#define OX820_SYS_CTRL_RST_MAC OX820_SYS_CTRL_RST_MACA
#define OX820_SYS_CTRL_RST_PCIEA 7
#define OX820_SYS_CTRL_RST_SGDMA 8
#define OX820_SYS_CTRL_RST_CIPHER 9
#define OX820_SYS_CTRL_RST_DDR 10
#define OX820_SYS_CTRL_RST_SATA 11
#define OX820_SYS_CTRL_RST_SATA_LINK 12
#define OX820_SYS_CTRL_RST_SATA_PHY 13
#define OX820_SYS_CTRL_RST_PCIEPHY 14
#define OX820_SYS_CTRL_RST_STATIC 15
#define OX820_SYS_CTRL_RST_GPIO 16
#define OX820_SYS_CTRL_RST_UART1 17
#define OX820_SYS_CTRL_RST_UART2 18
#define OX820_SYS_CTRL_RST_MISC 19
#define OX820_SYS_CTRL_RST_I2S 20
#define OX820_SYS_CTRL_RST_SD 21
#define OX820_SYS_CTRL_RST_MACB 22
#define OX820_SYS_CTRL_RST_PCIEB 23
#define OX820_SYS_CTRL_RST_VIDEO 24
#define OX820_SYS_CTRL_RST_DDR_PHY 25
#define OX820_SYS_CTRL_RST_USBHSPHYB 26
#define OX820_SYS_CTRL_RST_USBDEV 27
#define OX820_SYS_CTRL_RST_ARMDBG 29
#define OX820_SYS_CTRL_RST_PLLA 30
#define OX820_SYS_CTRL_RST_PLLB 31
/* bit numbers of clock control register */
#define OX820_SYS_CTRL_CLK_COPRO 0
#define OX820_SYS_CTRL_CLK_DMA 1
#define OX820_SYS_CTRL_CLK_CIPHER 2
#define OX820_SYS_CTRL_CLK_SD 3
#define OX820_SYS_CTRL_CLK_SATA 4
#define OX820_SYS_CTRL_CLK_I2S 5
#define OX820_SYS_CTRL_CLK_USBHS 6
#define OX820_SYS_CTRL_CLK_MACA 7
#define OX820_SYS_CTRL_CLK_MAC OX820_SYS_CTRL_CLK_MACA
#define OX820_SYS_CTRL_CLK_PCIEA 8
#define OX820_SYS_CTRL_CLK_STATIC 9
#define OX820_SYS_CTRL_CLK_MACB 10
#define OX820_SYS_CTRL_CLK_PCIEB 11
#define OX820_SYS_CTRL_CLK_REF600 12
#define OX820_SYS_CTRL_CLK_USBDEV 13
#define OX820_SYS_CTRL_CLK_DDR 14
#define OX820_SYS_CTRL_CLK_DDRPHY 15
#define OX820_SYS_CTRL_CLK_DDRCK 16
/* Regmap offsets */
#define OX820_CLK_SET_REGOFFSET 0x2c
#define OX820_CLK_CLR_REGOFFSET 0x30
#define OX820_RST_SET_REGOFFSET 0x34
#define OX820_RST_CLR_REGOFFSET 0x38
#define OX820_SECONDARY_SEL_REGOFFSET 0x14
#define OX820_TERTIARY_SEL_REGOFFSET 0x8c
#define OX820_QUATERNARY_SEL_REGOFFSET 0x94
#define OX820_DEBUG_SEL_REGOFFSET 0x9c
#define OX820_ALTERNATIVE_SEL_REGOFFSET 0xa4
#define OX820_PULLUP_SEL_REGOFFSET 0xac
#define OX820_SEC_SECONDARY_SEL_REGOFFSET 0x100014
#define OX820_SEC_TERTIARY_SEL_REGOFFSET 0x10008c
#define OX820_SEC_QUATERNARY_SEL_REGOFFSET 0x100094
#define OX820_SEC_DEBUG_SEL_REGOFFSET 0x10009c
#define OX820_SEC_ALTERNATIVE_SEL_REGOFFSET 0x1000a4
#define OX820_SEC_PULLUP_SEL_REGOFFSET 0x1000ac
struct oxnas_restart_context {
struct regmap *sys_ctrl;
struct notifier_block restart_handler;
};
static int ox820_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct oxnas_restart_context *ctx = container_of(this, struct
oxnas_restart_context,
restart_handler);
u32 value;
/*
* Assert reset to cores as per power on defaults
* Don't touch the DDR interface as things will come to an impromptu
* stop NB Possibly should be asserting reset for PLLB, but there are
* timing concerns here according to the docs
*/
value = BIT(OX820_SYS_CTRL_RST_COPRO) |
BIT(OX820_SYS_CTRL_RST_USBHS) |
BIT(OX820_SYS_CTRL_RST_USBHSPHYA) |
BIT(OX820_SYS_CTRL_RST_MACA) |
BIT(OX820_SYS_CTRL_RST_PCIEA) |
BIT(OX820_SYS_CTRL_RST_SGDMA) |
BIT(OX820_SYS_CTRL_RST_CIPHER) |
BIT(OX820_SYS_CTRL_RST_SATA) |
BIT(OX820_SYS_CTRL_RST_SATA_LINK) |
BIT(OX820_SYS_CTRL_RST_SATA_PHY) |
BIT(OX820_SYS_CTRL_RST_PCIEPHY) |
BIT(OX820_SYS_CTRL_RST_STATIC) |
BIT(OX820_SYS_CTRL_RST_UART1) |
BIT(OX820_SYS_CTRL_RST_UART2) |
BIT(OX820_SYS_CTRL_RST_MISC) |
BIT(OX820_SYS_CTRL_RST_I2S) |
BIT(OX820_SYS_CTRL_RST_SD) |
BIT(OX820_SYS_CTRL_RST_MACB) |
BIT(OX820_SYS_CTRL_RST_PCIEB) |
BIT(OX820_SYS_CTRL_RST_VIDEO) |
BIT(OX820_SYS_CTRL_RST_USBHSPHYB) |
BIT(OX820_SYS_CTRL_RST_USBDEV);
regmap_write(ctx->sys_ctrl, OX820_RST_SET_REGOFFSET, value);
/* Release reset to cores as per power on defaults */
regmap_write(ctx->sys_ctrl, OX820_RST_CLR_REGOFFSET,
BIT(OX820_SYS_CTRL_RST_GPIO));
/*
* Disable clocks to cores as per power-on defaults - must leave DDR
* related clocks enabled otherwise we'll stop rather abruptly.
*/
value = BIT(OX820_SYS_CTRL_CLK_COPRO) |
BIT(OX820_SYS_CTRL_CLK_DMA) |
BIT(OX820_SYS_CTRL_CLK_CIPHER) |
BIT(OX820_SYS_CTRL_CLK_SD) |
BIT(OX820_SYS_CTRL_CLK_SATA) |
BIT(OX820_SYS_CTRL_CLK_I2S) |
BIT(OX820_SYS_CTRL_CLK_USBHS) |
BIT(OX820_SYS_CTRL_CLK_MAC) |
BIT(OX820_SYS_CTRL_CLK_PCIEA) |
BIT(OX820_SYS_CTRL_CLK_STATIC) |
BIT(OX820_SYS_CTRL_CLK_MACB) |
BIT(OX820_SYS_CTRL_CLK_PCIEB) |
BIT(OX820_SYS_CTRL_CLK_REF600) |
BIT(OX820_SYS_CTRL_CLK_USBDEV);
regmap_write(ctx->sys_ctrl, OX820_CLK_CLR_REGOFFSET, value);
/* Enable clocks to cores as per power-on defaults */
/* Set sys-control pin mux'ing as per power-on defaults */
regmap_write(ctx->sys_ctrl, OX820_SECONDARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_TERTIARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_QUATERNARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_DEBUG_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_ALTERNATIVE_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_PULLUP_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_SECONDARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_TERTIARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_QUATERNARY_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_DEBUG_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_ALTERNATIVE_SEL_REGOFFSET, 0);
regmap_write(ctx->sys_ctrl, OX820_SEC_PULLUP_SEL_REGOFFSET, 0);
/*
* No need to save any state, as the ROM loader can determine whether
* reset is due to power cycling or programatic action, just hit the
* (self-clearing) CPU reset bit of the block reset register
*/
value =
BIT(OX820_SYS_CTRL_RST_SCU) |
BIT(OX820_SYS_CTRL_RST_ARM0) |
BIT(OX820_SYS_CTRL_RST_ARM1);
regmap_write(ctx->sys_ctrl, OX820_RST_SET_REGOFFSET, value);
pr_emerg("Unable to restart system\n");
return NOTIFY_DONE;
}
static int ox820_restart_probe(struct platform_device *pdev)
{
struct oxnas_restart_context *ctx;
struct regmap *sys_ctrl;
struct device *dev = &pdev->dev;
int err = 0;
sys_ctrl = syscon_node_to_regmap(pdev->dev.of_node);
if (IS_ERR(sys_ctrl))
return PTR_ERR(sys_ctrl);
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->sys_ctrl = sys_ctrl;
ctx->restart_handler.notifier_call = ox820_restart_handle;
ctx->restart_handler.priority = 192;
err = register_restart_handler(&ctx->restart_handler);
if (err)
dev_err(dev, "can't register restart notifier (err=%d)\n", err);
return err;
}
static const struct of_device_id ox820_restart_of_match[] = {
{ .compatible = "oxsemi,ox820-sys-ctrl" },
{}
};
static struct platform_driver ox820_restart_driver = {
.probe = ox820_restart_probe,
.driver = {
.name = "ox820-chip-reset",
.of_match_table = ox820_restart_of_match,
},
};
builtin_platform_driver(ox820_restart_driver);

View File

@ -34,7 +34,8 @@ static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot,
ret = regmap_update_bits(pon->regmap,
pon->baseaddr + PON_SOFT_RB_SPARE,
0xfc, magic << pon->reason_shift);
GENMASK(7, pon->reason_shift),
magic << pon->reason_shift);
if (ret < 0)
dev_err(pon->dev, "update reboot mode bits failed\n");

View File

@ -51,8 +51,11 @@ static int syscon_reboot_probe(struct platform_device *pdev)
return -ENOMEM;
ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
if (IS_ERR(ctx->map))
return PTR_ERR(ctx->map);
if (IS_ERR(ctx->map)) {
ctx->map = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(ctx->map))
return PTR_ERR(ctx->map);
}
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
return -EINVAL;

View File

@ -919,16 +919,12 @@ static int pm860x_battery_probe(struct platform_device *pdev)
return -ENOMEM;
info->irq_cc = platform_get_irq(pdev, 0);
if (info->irq_cc <= 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
if (info->irq_cc <= 0)
return -EINVAL;
}
info->irq_batt = platform_get_irq(pdev, 1);
if (info->irq_batt <= 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
if (info->irq_batt <= 0)
return -EINVAL;
}
info->chip = chip;
info->i2c =

View File

@ -116,6 +116,17 @@ config BATTERY_CPCAP
Say Y here to enable support for battery on Motorola
phones and tablets such as droid 4.
config BATTERY_CW2015
tristate "CW2015 Battery driver"
depends on I2C
select REGMAP_I2C
help
Say Y here to enable support for the cellwise cw2015
battery fuel gauge (used in the Pinebook Pro & others)
This driver can also be built as a module. If so, the module will be
called cw2015_battery.
config BATTERY_DS2760
tristate "DS2760 battery driver (HP iPAQ & others)"
depends on W1
@ -415,7 +426,7 @@ config CHARGER_PCF50633
tristate "NXP PCF50633 MBC"
depends on MFD_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
Say Y to include support for NXP PCF50633 Main Battery Charger.
config BATTERY_RX51
tristate "Nokia RX-51 (N900) battery driver"
@ -587,7 +598,7 @@ config CHARGER_BQ24257
tristate "TI BQ24250/24251/24257 battery charger driver"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
depends on REGMAP_I2C
select REGMAP_I2C
help
Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery
chargers.
@ -619,15 +630,15 @@ config CHARGER_TPS65090
tristate "TPS65090 battery charger driver"
depends on MFD_TPS65090
help
Say Y here to enable support for battery charging with TPS65090
PMIC chips.
Say Y here to enable support for battery charging with TPS65090
PMIC chips.
config CHARGER_TPS65217
tristate "TPS65217 battery charger driver"
depends on MFD_TPS65217
help
Say Y here to enable support for battery charging with TPS65217
PMIC chips.
Say Y here to enable support for battery charging with TPS65217
PMIC chips.
config BATTERY_GAUGE_LTC2941
tristate "LTC2941/LTC2943 Battery Gauge Driver"
@ -670,7 +681,6 @@ config CHARGER_RT9455
config CHARGER_CROS_USBPD
tristate "ChromeOS EC based USBPD charger"
depends on CROS_USBPD_NOTIFY
default n
help
Say Y here to enable ChromeOS EC based USBPD charger
driver. This driver gets various bits of information about
@ -681,16 +691,16 @@ config CHARGER_SC2731
tristate "Spreadtrum SC2731 charger driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
help
Say Y here to enable support for battery charging with SC2731
PMIC chips.
Say Y here to enable support for battery charging with SC2731
PMIC chips.
config FUEL_GAUGE_SC27XX
tristate "Spreadtrum SC27XX fuel gauge driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
depends on IIO
help
Say Y here to enable support for fuel gauge with SC27XX
PMIC chips.
Say Y here to enable support for fuel gauge with SC27XX
PMIC chips.
config CHARGER_UCS1002
tristate "Microchip UCS1002 USB Port Power Controller"
@ -705,11 +715,20 @@ config CHARGER_UCS1002
config CHARGER_BD70528
tristate "ROHM bd70528 charger driver"
depends on MFD_ROHM_BD70528
default n
select LINEAR_RANGES
help
Say Y here to enable support for getting battery status
information and altering charger configurations from charger
block of the ROHM BD70528 Power Management IC.
Say Y here to enable support for getting battery status
information and altering charger configurations from charger
block of the ROHM BD70528 Power Management IC.
config CHARGER_BD99954
tristate "ROHM bd99954 charger driver"
depends on I2C
select LINEAR_RANGES
help
Say Y here to enable support for getting battery and charger
information and altering charger configurations from the ROHM
BD99954 charger IC.
config CHARGER_WILCO
tristate "Wilco EC based charger for ChromeOS"

View File

@ -24,6 +24,7 @@ obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o
obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
obj-$(CONFIG_BATTERY_CPCAP) += cpcap-battery.o
obj-$(CONFIG_BATTERY_CW2015) += cw2015_battery.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
@ -92,4 +93,5 @@ obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o
obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o

View File

@ -2399,7 +2399,7 @@ static void ab8500_fg_reinit_work(struct work_struct *work)
struct ab8500_fg *di = container_of(work, struct ab8500_fg,
fg_reinit_work.work);
if (di->flags.calibrate == false) {
if (!di->flags.calibrate) {
dev_dbg(di->dev, "Resetting FG state machine to init.\n");
ab8500_fg_clear_cap_samples(di);
ab8500_fg_calc_cap_discharge_voltage(di, true);

View File

@ -880,10 +880,9 @@ static int axp288_charger_probe(struct platform_device *pdev)
/* Register charger interrupts */
for (i = 0; i < CHRG_INTR_END; i++) {
pirq = platform_get_irq(info->pdev, i);
if (pirq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pirq);
if (pirq < 0)
return pirq;
}
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
if (info->irq[i] < 0) {
dev_warn(&info->pdev->dev,

View File

@ -717,6 +717,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
},
},
{
/* Meegopad T02 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"),
},
},
{
/* Meegopad T08 */
.matches = {

View File

@ -72,6 +72,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/linear_range.h>
#define CHG_STAT_SUSPEND 0x0
#define CHG_STAT_TRICKLE 0x1
@ -335,38 +336,37 @@ static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val)
return 0;
}
struct bd70528_linear_range {
int min;
int step;
int vals;
int low_sel;
};
static const struct bd70528_linear_range current_limit_ranges[] = {
static const struct linear_range current_limit_ranges[] = {
{
.min = 5,
.step = 1,
.vals = 36,
.low_sel = 0,
.min_sel = 0,
.max_sel = 0x22,
},
{
.min = 40,
.step = 5,
.vals = 5,
.low_sel = 0x23,
.min_sel = 0x23,
.max_sel = 0x26,
},
{
.min = 60,
.step = 20,
.vals = 8,
.low_sel = 0x27,
.min_sel = 0x27,
.max_sel = 0x2d,
},
{
.min = 200,
.step = 50,
.vals = 7,
.low_sel = 0x2e,
}
.min_sel = 0x2e,
.max_sel = 0x34,
},
{
.min = 500,
.step = 0,
.min_sel = 0x35,
.max_sel = 0x3f,
},
};
/*
@ -374,18 +374,18 @@ static const struct bd70528_linear_range current_limit_ranges[] = {
* voltage for low temperatures. The driver currently only reads
* the charge current at room temperature. We do set both though.
*/
static const struct bd70528_linear_range warm_charge_curr[] = {
static const struct linear_range warm_charge_curr[] = {
{
.min = 10,
.step = 10,
.vals = 20,
.low_sel = 0,
.min_sel = 0,
.max_sel = 0x12,
},
{
.min = 200,
.step = 25,
.vals = 13,
.low_sel = 0x13,
.min_sel = 0x13,
.max_sel = 0x1f,
},
};
@ -398,56 +398,6 @@ static const struct bd70528_linear_range warm_charge_curr[] = {
#define MAX_WARM_CHG_CURR_SEL 0x1f
#define MIN_CHG_CURR_SEL 0x0
static int find_value_for_selector_low(const struct bd70528_linear_range *r,
int selectors, unsigned int sel,
unsigned int *val)
{
int i;
for (i = 0; i < selectors; i++) {
if (r[i].low_sel <= sel && r[i].low_sel + r[i].vals >= sel) {
*val = r[i].min + (sel - r[i].low_sel) * r[i].step;
return 0;
}
}
return -EINVAL;
}
/*
* For BD70528 voltage/current limits we happily accept any value which
* belongs the range. We could check if value matching the selector is
* desired by computing the range min + (sel - sel_low) * range step - but
* I guess it is enough if we use voltage/current which is closest (below)
* the requested?
*/
static int find_selector_for_value_low(const struct bd70528_linear_range *r,
int selectors, unsigned int val,
unsigned int *sel, bool *found)
{
int i;
int ret = -EINVAL;
*found = false;
for (i = 0; i < selectors; i++) {
if (r[i].min <= val) {
if (r[i].min + r[i].step * r[i].vals >= val) {
*found = true;
*sel = r[i].low_sel + (val - r[i].min) /
r[i].step;
ret = 0;
break;
}
/*
* If the range max is smaller than requested
* we can set the max supported value from range
*/
*sel = r[i].low_sel + r[i].vals;
ret = 0;
}
}
return ret;
}
static int get_charge_current(struct bd70528_psy *bdpsy, int *ma)
{
unsigned int sel;
@ -463,9 +413,9 @@ static int get_charge_current(struct bd70528_psy *bdpsy, int *ma)
sel &= BD70528_MASK_CHG_CHG_CURR;
ret = find_value_for_selector_low(&warm_charge_curr[0],
ARRAY_SIZE(warm_charge_curr), sel,
ma);
ret = linear_range_get_value_array(&warm_charge_curr[0],
ARRAY_SIZE(warm_charge_curr),
sel, ma);
if (ret) {
dev_err(bdpsy->dev,
"Unknown charge current value 0x%x\n",
@ -491,10 +441,9 @@ static int get_current_limit(struct bd70528_psy *bdpsy, int *ma)
sel &= BD70528_MASK_CHG_DCIN_ILIM;
ret = find_value_for_selector_low(&current_limit_ranges[0],
ARRAY_SIZE(current_limit_ranges), sel,
ma);
ret = linear_range_get_value_array(&current_limit_ranges[0],
ARRAY_SIZE(current_limit_ranges),
sel, ma);
if (ret) {
/* Unspecified values mean 500 mA */
*ma = 500;
@ -588,15 +537,28 @@ static int set_charge_current(struct bd70528_psy *bdpsy, int ma)
goto set;
}
ret = find_selector_for_value_low(&warm_charge_curr[0],
ARRAY_SIZE(warm_charge_curr), ma,
&reg, &found);
/*
* For BD70528 voltage/current limits we happily accept any value which
* belongs the range. We could check if value matching the selector is
* desired by computing the range min + (sel - sel_low) * range step - but
* I guess it is enough if we use voltage/current which is closest (below)
* the requested?
*/
ret = linear_range_get_selector_low_array(warm_charge_curr,
ARRAY_SIZE(warm_charge_curr),
ma, &reg, &found);
if (ret) {
dev_err(bdpsy->dev,
"Unsupported charge current %u mA\n", ma);
reg = MIN_CHG_CURR_SEL;
goto set;
}
if (!found) {
/* There was a gap in supported values and we hit it */
/*
* There was a gap in supported values and we hit it.
* Yet a smaller value was found so we use it.
*/
dev_warn(bdpsy->dev,
"Unsupported charge current %u mA\n", ma);
}
@ -648,17 +610,21 @@ static int set_current_limit(struct bd70528_psy *bdpsy, int ma)
goto set;
}
ret = find_selector_for_value_low(&current_limit_ranges[0],
ARRAY_SIZE(current_limit_ranges), ma,
&reg, &found);
ret = linear_range_get_selector_low_array(current_limit_ranges,
ARRAY_SIZE(current_limit_ranges),
ma, &reg, &found);
if (ret) {
dev_err(bdpsy->dev, "Unsupported current limit %umA\n", ma);
reg = MIN_CURR_LIMIT_SEL;
goto set;
}
if (!found) {
/* There was a gap in supported values and we hit it ?*/
dev_warn(bdpsy->dev, "Unsupported current limit %umA\n",
ma);
/*
* There was a gap in supported values and we hit it.
* We found a smaller value from ranges and use it.
* Warn user though.
*/
dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", ma);
}
set:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -673,7 +673,7 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi)
* { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq };
* struct i2c_adapter ad = { ... };
* i2c_add_adapter(&ad);
* i2c_new_device(&ad, &bi);
* i2c_new_client_device(&ad, &bi);
*/
if (device_property_read_bool(bdi->dev, "disable-reset"))
return 0;

View File

@ -32,6 +32,13 @@ enum bq25890_chip_version {
BQ25896,
};
static const char *const bq25890_chip_name[] = {
"BQ25890",
"BQ25892",
"BQ25895",
"BQ25896",
};
enum bq25890_fields {
F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */
F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */
@ -119,6 +126,7 @@ static const struct regmap_access_table bq25890_writeable_regs = {
static const struct regmap_range bq25890_volatile_reg_ranges[] = {
regmap_reg_range(0x00, 0x00),
regmap_reg_range(0x02, 0x02),
regmap_reg_range(0x09, 0x09),
regmap_reg_range(0x0b, 0x14),
};
@ -246,6 +254,7 @@ enum bq25890_table_ids {
/* range tables */
TBL_ICHG,
TBL_ITERM,
TBL_IILIM,
TBL_VREG,
TBL_BOOSTV,
TBL_SYSVMIN,
@ -286,6 +295,7 @@ static const union {
/* TODO: BQ25896 has max ICHG 3008 mA */
[TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
[TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
[TBL_IILIM] = { .rt = {50000, 3200000, 50000} }, /* uA */
[TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
[TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */
[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */
@ -367,18 +377,42 @@ enum bq25890_chrg_fault {
CHRG_FAULT_TIMER_EXPIRED,
};
static bool bq25890_is_adc_property(enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_CURRENT_NOW:
return true;
default:
return false;
}
}
static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq);
static int bq25890_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret;
struct bq25890_device *bq = power_supply_get_drvdata(psy);
struct bq25890_state state;
bool do_adc_conv;
int ret;
mutex_lock(&bq->lock);
/* update state in case we lost an interrupt */
__bq25890_handle_irq(bq);
state = bq->state;
do_adc_conv = !state.online && bq25890_is_adc_property(psp);
if (do_adc_conv)
bq25890_field_write(bq, F_CONV_START, 1);
mutex_unlock(&bq->lock);
if (do_adc_conv)
regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START],
ret, !ret, 25000, 1000000);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (!state.online)
@ -395,22 +429,24 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
if (!state.online || state.chrg_status == STATUS_NOT_CHARGING ||
state.chrg_status == STATUS_TERMINATION_DONE)
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
else if (state.chrg_status == STATUS_PRE_CHARGING)
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
else if (state.chrg_status == STATUS_FAST_CHARGING)
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
else /* unreachable */
val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = BQ25890_MANUFACTURER;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
if (bq->chip_version == BQ25890)
val->strval = "BQ25890";
else if (bq->chip_version == BQ25892)
val->strval = "BQ25892";
else if (bq->chip_version == BQ25895)
val->strval = "BQ25895";
else if (bq->chip_version == BQ25896)
val->strval = "BQ25896";
else
val->strval = "UNKNOWN";
val->strval = bq25890_chip_name[bq->chip_version];
break;
case POWER_SUPPLY_PROP_ONLINE:
@ -430,15 +466,6 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
if (ret < 0)
return ret;
/* converted_val = ADC_val * 50mA (table 10.3.19) */
val->intval = ret * 50000;
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG);
break;
@ -461,10 +488,22 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG);
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq25890_field_read(bq, F_IILIM);
if (ret < 0)
return ret;
val->intval = bq25890_find_val(ret, TBL_IILIM);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bq25890_field_read(bq, F_SYSV); /* read measured value */
if (ret < 0)
@ -474,6 +513,15 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
val->intval = 2304000 + ret * 20000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
if (ret < 0)
return ret;
/* converted_val = ADC_val * 50mA (table 10.3.19) */
val->intval = ret * -50000;
break;
default:
return -EINVAL;
}
@ -513,74 +561,50 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
return 0;
}
static bool bq25890_state_changed(struct bq25890_device *bq,
struct bq25890_state *new_state)
{
struct bq25890_state old_state;
mutex_lock(&bq->lock);
old_state = bq->state;
mutex_unlock(&bq->lock);
return (old_state.chrg_status != new_state->chrg_status ||
old_state.chrg_fault != new_state->chrg_fault ||
old_state.online != new_state->online ||
old_state.bat_fault != new_state->bat_fault ||
old_state.boost_fault != new_state->boost_fault ||
old_state.vsys_status != new_state->vsys_status);
}
static void bq25890_handle_state_change(struct bq25890_device *bq,
struct bq25890_state *new_state)
static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq)
{
struct bq25890_state new_state;
int ret;
struct bq25890_state old_state;
mutex_lock(&bq->lock);
old_state = bq->state;
mutex_unlock(&bq->lock);
ret = bq25890_get_chip_state(bq, &new_state);
if (ret < 0)
return IRQ_NONE;
if (!new_state->online) { /* power removed */
if (!memcmp(&bq->state, &new_state, sizeof(new_state)))
return IRQ_NONE;
if (!new_state.online && bq->state.online) { /* power removed */
/* disable ADC */
ret = bq25890_field_write(bq, F_CONV_START, 0);
if (ret < 0)
goto error;
} else if (!old_state.online) { /* power inserted */
} else if (new_state.online && !bq->state.online) { /* power inserted */
/* enable ADC, to have control of charge current/voltage */
ret = bq25890_field_write(bq, F_CONV_START, 1);
if (ret < 0)
goto error;
}
return;
bq->state = new_state;
power_supply_changed(bq->charger);
return IRQ_HANDLED;
error:
dev_err(bq->dev, "Error communicating with the chip.\n");
dev_err(bq->dev, "Error communicating with the chip: %pe\n",
ERR_PTR(ret));
return IRQ_HANDLED;
}
static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
{
struct bq25890_device *bq = private;
int ret;
struct bq25890_state state;
ret = bq25890_get_chip_state(bq, &state);
if (ret < 0)
goto handled;
if (!bq25890_state_changed(bq, &state))
goto handled;
bq25890_handle_state_change(bq, &state);
irqreturn_t ret;
mutex_lock(&bq->lock);
bq->state = state;
ret = __bq25890_handle_irq(bq);
mutex_unlock(&bq->lock);
power_supply_changed(bq->charger);
handled:
return IRQ_HANDLED;
return ret;
}
static int bq25890_chip_reset(struct bq25890_device *bq)
@ -610,7 +634,6 @@ static int bq25890_hw_init(struct bq25890_device *bq)
{
int ret;
int i;
struct bq25890_state state;
const struct {
enum bq25890_fields id;
@ -651,38 +674,37 @@ static int bq25890_hw_init(struct bq25890_device *bq)
}
}
/* Configure ADC for continuous conversions. This does not enable it. */
ret = bq25890_field_write(bq, F_CONV_RATE, 1);
/* Configure ADC for continuous conversions when charging */
ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online);
if (ret < 0) {
dev_dbg(bq->dev, "Config ADC failed %d\n", ret);
return ret;
}
ret = bq25890_get_chip_state(bq, &state);
ret = bq25890_get_chip_state(bq, &bq->state);
if (ret < 0) {
dev_dbg(bq->dev, "Get state failed %d\n", ret);
return ret;
}
mutex_lock(&bq->lock);
bq->state = state;
mutex_unlock(&bq->lock);
return 0;
}
static enum power_supply_property bq25890_power_supply_props[] = {
static const enum power_supply_property bq25890_power_supply_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_HEALTH,
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_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static char *bq25890_charger_supplied_to[] = {
@ -881,17 +903,11 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
static int bq25890_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev;
struct bq25890_device *bq;
int ret;
int i;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
return -ENODEV;
}
bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
if (!bq)
return -ENOMEM;
@ -1004,34 +1020,34 @@ static int bq25890_suspend(struct device *dev)
* If charger is removed, while in suspend, make sure ADC is diabled
* since it consumes slightly more power.
*/
return bq25890_field_write(bq, F_CONV_START, 0);
return bq25890_field_write(bq, F_CONV_RATE, 0);
}
static int bq25890_resume(struct device *dev)
{
int ret;
struct bq25890_state state;
struct bq25890_device *bq = dev_get_drvdata(dev);
ret = bq25890_get_chip_state(bq, &state);
if (ret < 0)
return ret;
mutex_lock(&bq->lock);
bq->state = state;
mutex_unlock(&bq->lock);
ret = bq25890_get_chip_state(bq, &bq->state);
if (ret < 0)
goto unlock;
/* Re-enable ADC only if charger is plugged in. */
if (state.online) {
ret = bq25890_field_write(bq, F_CONV_START, 1);
if (bq->state.online) {
ret = bq25890_field_write(bq, F_CONV_RATE, 1);
if (ret < 0)
return ret;
goto unlock;
}
/* signal userspace, maybe state changed while suspended */
power_supply_changed(bq->charger);
return 0;
unlock:
mutex_unlock(&bq->lock);
return ret;
}
#endif

View File

@ -1422,7 +1422,9 @@ static int charger_manager_prepare_sysfs(struct charger_manager *cm)
}
static int cm_init_thermal_data(struct charger_manager *cm,
struct power_supply *fuel_gauge)
struct power_supply *fuel_gauge,
enum power_supply_property *properties,
size_t *num_properties)
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
@ -1433,9 +1435,8 @@ static int cm_init_thermal_data(struct charger_manager *cm,
POWER_SUPPLY_PROP_TEMP, &val);
if (!ret) {
cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy_desc.num_properties++;
properties[*num_properties] = POWER_SUPPLY_PROP_TEMP;
(*num_properties)++;
cm->desc->measure_battery_temp = true;
}
#ifdef CONFIG_THERMAL
@ -1446,9 +1447,8 @@ static int cm_init_thermal_data(struct charger_manager *cm,
return PTR_ERR(cm->tzd_batt);
/* Use external thermometer */
cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy_desc.num_properties++;
properties[*num_properties] = POWER_SUPPLY_PROP_TEMP_AMBIENT;
(*num_properties)++;
cm->desc->measure_battery_temp = true;
ret = 0;
}
@ -1621,6 +1621,8 @@ static int charger_manager_probe(struct platform_device *pdev)
int j = 0;
union power_supply_propval val;
struct power_supply *fuel_gauge;
enum power_supply_property *properties;
size_t num_properties;
struct power_supply_config psy_cfg = {};
if (IS_ERR(desc)) {
@ -1717,18 +1719,17 @@ static int charger_manager_probe(struct platform_device *pdev)
cm->charger_psy_desc.name = cm->psy_name_buf;
/* Allocate for psy properties because they may vary */
cm->charger_psy_desc.properties =
devm_kcalloc(&pdev->dev,
properties = devm_kcalloc(&pdev->dev,
ARRAY_SIZE(default_charger_props) +
NUM_CHARGER_PSY_OPTIONAL,
sizeof(enum power_supply_property), GFP_KERNEL);
if (!cm->charger_psy_desc.properties)
sizeof(*properties), GFP_KERNEL);
if (!properties)
return -ENOMEM;
memcpy(cm->charger_psy_desc.properties, default_charger_props,
memcpy(properties, default_charger_props,
sizeof(enum power_supply_property) *
ARRAY_SIZE(default_charger_props));
cm->charger_psy_desc.num_properties = psy_default.num_properties;
num_properties = ARRAY_SIZE(default_charger_props);
/* Find which optional psy-properties are available */
fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
@ -1739,25 +1740,28 @@ static int charger_manager_probe(struct platform_device *pdev)
}
if (!power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
properties[num_properties] =
POWER_SUPPLY_PROP_CHARGE_NOW;
cm->charger_psy_desc.num_properties++;
num_properties++;
}
if (!power_supply_get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW,
&val)) {
cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
properties[num_properties] =
POWER_SUPPLY_PROP_CURRENT_NOW;
cm->charger_psy_desc.num_properties++;
num_properties++;
}
ret = cm_init_thermal_data(cm, fuel_gauge);
ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->desc->measure_battery_temp = false;
}
power_supply_put(fuel_gauge);
cm->charger_psy_desc.properties = properties;
cm->charger_psy_desc.num_properties = num_properties;
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
/* Register sysfs entry for charger(regulator) */

View File

@ -0,0 +1,750 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Fuel gauge driver for CellWise 2013 / 2015
*
* Copyright (C) 2012, RockChip
* Copyright (C) 2020, Tobias Schramm
*
* Authors: xuhuicong <xhc@rock-chips.com>
* Authors: Tobias Schramm <t.schramm@manjaro.org>
*/
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/workqueue.h>
#define CW2015_SIZE_BATINFO 64
#define CW2015_RESET_TRIES 5
#define CW2015_REG_VERSION 0x00
#define CW2015_REG_VCELL 0x02
#define CW2015_REG_SOC 0x04
#define CW2015_REG_RRT_ALERT 0x06
#define CW2015_REG_CONFIG 0x08
#define CW2015_REG_MODE 0x0A
#define CW2015_REG_BATINFO 0x10
#define CW2015_MODE_SLEEP_MASK GENMASK(7, 6)
#define CW2015_MODE_SLEEP (0x03 << 6)
#define CW2015_MODE_NORMAL (0x00 << 6)
#define CW2015_MODE_QUICK_START (0x03 << 4)
#define CW2015_MODE_RESTART (0x0f << 0)
#define CW2015_CONFIG_UPDATE_FLG (0x01 << 1)
#define CW2015_ATHD(x) ((x) << 3)
#define CW2015_MASK_ATHD GENMASK(7, 3)
#define CW2015_MASK_SOC GENMASK(12, 0)
/* reset gauge of no valid state of charge could be polled for 40s */
#define CW2015_BAT_SOC_ERROR_MS (40 * MSEC_PER_SEC)
/* reset gauge if state of charge stuck for half an hour during charging */
#define CW2015_BAT_CHARGING_STUCK_MS (1800 * MSEC_PER_SEC)
/* poll interval from CellWise GPL Android driver example */
#define CW2015_DEFAULT_POLL_INTERVAL_MS 8000
#define CW2015_AVERAGING_SAMPLES 3
struct cw_battery {
struct device *dev;
struct workqueue_struct *battery_workqueue;
struct delayed_work battery_delay_work;
struct regmap *regmap;
struct power_supply *rk_bat;
struct power_supply_battery_info battery;
u8 *bat_profile;
bool charger_attached;
bool battery_changed;
int soc;
int voltage_mv;
int status;
int time_to_empty;
int charge_count;
u32 poll_interval_ms;
u8 alert_level;
unsigned int read_errors;
unsigned int charge_stuck_cnt;
};
static int cw_read_word(struct cw_battery *cw_bat, u8 reg, u16 *val)
{
__be16 value;
int ret;
ret = regmap_bulk_read(cw_bat->regmap, reg, &value, sizeof(value));
if (ret)
return ret;
*val = be16_to_cpu(value);
return 0;
}
static int cw_update_profile(struct cw_battery *cw_bat)
{
int ret;
unsigned int reg_val;
u8 reset_val;
/* make sure gauge is not in sleep mode */
ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, &reg_val);
if (ret)
return ret;
reset_val = reg_val;
if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
dev_err(cw_bat->dev,
"Gauge is in sleep mode, can't update battery info\n");
return -EINVAL;
}
/* write new battery info */
ret = regmap_raw_write(cw_bat->regmap, CW2015_REG_BATINFO,
cw_bat->bat_profile,
CW2015_SIZE_BATINFO);
if (ret)
return ret;
/* set config update flag */
reg_val |= CW2015_CONFIG_UPDATE_FLG;
reg_val &= ~CW2015_MASK_ATHD;
reg_val |= CW2015_ATHD(cw_bat->alert_level);
ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val);
if (ret)
return ret;
/* reset gauge to apply new battery profile */
reset_val &= ~CW2015_MODE_RESTART;
reg_val = reset_val | CW2015_MODE_RESTART;
ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val);
if (ret)
return ret;
/* wait for gauge to reset */
msleep(20);
/* clear reset flag */
ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
if (ret)
return ret;
/* wait for gauge to become ready */
ret = regmap_read_poll_timeout(cw_bat->regmap, CW2015_REG_SOC,
reg_val, reg_val <= 100,
10 * USEC_PER_MSEC, 10 * USEC_PER_SEC);
if (ret)
dev_err(cw_bat->dev,
"Gauge did not become ready after profile upload\n");
else
dev_dbg(cw_bat->dev, "Battery profile updated\n");
return ret;
}
static int cw_init(struct cw_battery *cw_bat)
{
int ret;
unsigned int reg_val = CW2015_MODE_SLEEP;
if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
reg_val = CW2015_MODE_NORMAL;
ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reg_val);
if (ret)
return ret;
}
ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, &reg_val);
if (ret)
return ret;
if ((reg_val & CW2015_MASK_ATHD) != CW2015_ATHD(cw_bat->alert_level)) {
dev_dbg(cw_bat->dev, "Setting new alert level\n");
reg_val &= ~CW2015_MASK_ATHD;
reg_val |= ~CW2015_ATHD(cw_bat->alert_level);
ret = regmap_write(cw_bat->regmap, CW2015_REG_CONFIG, reg_val);
if (ret)
return ret;
}
ret = regmap_read(cw_bat->regmap, CW2015_REG_CONFIG, &reg_val);
if (ret)
return ret;
if (!(reg_val & CW2015_CONFIG_UPDATE_FLG)) {
dev_dbg(cw_bat->dev,
"Battery profile not present, uploading battery profile\n");
if (cw_bat->bat_profile) {
ret = cw_update_profile(cw_bat);
if (ret) {
dev_err(cw_bat->dev,
"Failed to upload battery profile\n");
return ret;
}
} else {
dev_warn(cw_bat->dev,
"No profile specified, continuing without profile\n");
}
} else if (cw_bat->bat_profile) {
u8 bat_info[CW2015_SIZE_BATINFO];
ret = regmap_raw_read(cw_bat->regmap, CW2015_REG_BATINFO,
bat_info, CW2015_SIZE_BATINFO);
if (ret) {
dev_err(cw_bat->dev,
"Failed to read stored battery profile\n");
return ret;
}
if (memcmp(bat_info, cw_bat->bat_profile, CW2015_SIZE_BATINFO)) {
dev_warn(cw_bat->dev, "Replacing stored battery profile\n");
ret = cw_update_profile(cw_bat);
if (ret)
return ret;
}
} else {
dev_warn(cw_bat->dev,
"Can't check current battery profile, no profile provided\n");
}
dev_dbg(cw_bat->dev, "Battery profile configured\n");
return 0;
}
static int cw_power_on_reset(struct cw_battery *cw_bat)
{
int ret;
unsigned char reset_val;
reset_val = CW2015_MODE_SLEEP;
ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
if (ret)
return ret;
/* wait for gauge to enter sleep */
msleep(20);
reset_val = CW2015_MODE_NORMAL;
ret = regmap_write(cw_bat->regmap, CW2015_REG_MODE, reset_val);
if (ret)
return ret;
ret = cw_init(cw_bat);
if (ret)
return ret;
return 0;
}
#define HYSTERESIS(current, previous, up, down) \
(((current) < (previous) + (up)) && ((current) > (previous) - (down)))
static int cw_get_soc(struct cw_battery *cw_bat)
{
unsigned int soc;
int ret;
ret = regmap_read(cw_bat->regmap, CW2015_REG_SOC, &soc);
if (ret)
return ret;
if (soc > 100) {
int max_error_cycles =
CW2015_BAT_SOC_ERROR_MS / cw_bat->poll_interval_ms;
dev_err(cw_bat->dev, "Invalid SoC %d%%\n", soc);
cw_bat->read_errors++;
if (cw_bat->read_errors > max_error_cycles) {
dev_warn(cw_bat->dev,
"Too many invalid SoC reports, resetting gauge\n");
cw_power_on_reset(cw_bat);
cw_bat->read_errors = 0;
}
return cw_bat->soc;
}
cw_bat->read_errors = 0;
/* Reset gauge if stuck while charging */
if (cw_bat->status == POWER_SUPPLY_STATUS_CHARGING && soc == cw_bat->soc) {
int max_stuck_cycles =
CW2015_BAT_CHARGING_STUCK_MS / cw_bat->poll_interval_ms;
cw_bat->charge_stuck_cnt++;
if (cw_bat->charge_stuck_cnt > max_stuck_cycles) {
dev_warn(cw_bat->dev,
"SoC stuck @%u%%, resetting gauge\n", soc);
cw_power_on_reset(cw_bat);
cw_bat->charge_stuck_cnt = 0;
}
} else {
cw_bat->charge_stuck_cnt = 0;
}
/* Ignore voltage dips during charge */
if (cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 0, 3))
soc = cw_bat->soc;
/* Ignore voltage spikes during discharge */
if (!cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 3, 0))
soc = cw_bat->soc;
return soc;
}
static int cw_get_voltage(struct cw_battery *cw_bat)
{
int ret, i, voltage_mv;
u16 reg_val;
u32 avg = 0;
for (i = 0; i < CW2015_AVERAGING_SAMPLES; i++) {
ret = cw_read_word(cw_bat, CW2015_REG_VCELL, &reg_val);
if (ret)
return ret;
avg += reg_val;
}
avg /= CW2015_AVERAGING_SAMPLES;
/*
* 305 uV per ADC step
* Use 312 / 1024 as efficient approximation of 305 / 1000
* Negligible error of 0.1%
*/
voltage_mv = avg * 312 / 1024;
dev_dbg(cw_bat->dev, "Read voltage: %d mV, raw=0x%04x\n",
voltage_mv, reg_val);
return voltage_mv;
}
static int cw_get_time_to_empty(struct cw_battery *cw_bat)
{
int ret;
u16 value16;
ret = cw_read_word(cw_bat, CW2015_REG_RRT_ALERT, &value16);
if (ret)
return ret;
return value16 & CW2015_MASK_SOC;
}
static void cw_update_charge_status(struct cw_battery *cw_bat)
{
int ret;
ret = power_supply_am_i_supplied(cw_bat->rk_bat);
if (ret < 0) {
dev_warn(cw_bat->dev, "Failed to get supply state: %d\n", ret);
} else {
bool charger_attached;
charger_attached = !!ret;
if (cw_bat->charger_attached != charger_attached) {
cw_bat->battery_changed = true;
if (charger_attached)
cw_bat->charge_count++;
}
cw_bat->charger_attached = charger_attached;
}
}
static void cw_update_soc(struct cw_battery *cw_bat)
{
int soc;
soc = cw_get_soc(cw_bat);
if (soc < 0)
dev_err(cw_bat->dev, "Failed to get SoC from gauge: %d\n", soc);
else if (cw_bat->soc != soc) {
cw_bat->soc = soc;
cw_bat->battery_changed = true;
}
}
static void cw_update_voltage(struct cw_battery *cw_bat)
{
int voltage_mv;
voltage_mv = cw_get_voltage(cw_bat);
if (voltage_mv < 0)
dev_err(cw_bat->dev, "Failed to get voltage from gauge: %d\n",
voltage_mv);
else
cw_bat->voltage_mv = voltage_mv;
}
static void cw_update_status(struct cw_battery *cw_bat)
{
int status = POWER_SUPPLY_STATUS_DISCHARGING;
if (cw_bat->charger_attached) {
if (cw_bat->soc >= 100)
status = POWER_SUPPLY_STATUS_FULL;
else
status = POWER_SUPPLY_STATUS_CHARGING;
}
if (cw_bat->status != status)
cw_bat->battery_changed = true;
cw_bat->status = status;
}
static void cw_update_time_to_empty(struct cw_battery *cw_bat)
{
int time_to_empty;
time_to_empty = cw_get_time_to_empty(cw_bat);
if (time_to_empty < 0)
dev_err(cw_bat->dev, "Failed to get time to empty from gauge: %d\n",
time_to_empty);
else if (cw_bat->time_to_empty != time_to_empty) {
cw_bat->time_to_empty = time_to_empty;
cw_bat->battery_changed = true;
}
}
static void cw_bat_work(struct work_struct *work)
{
struct delayed_work *delay_work;
struct cw_battery *cw_bat;
int ret;
unsigned int reg_val;
delay_work = to_delayed_work(work);
cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work);
ret = regmap_read(cw_bat->regmap, CW2015_REG_MODE, &reg_val);
if (ret) {
dev_err(cw_bat->dev, "Failed to read mode from gauge: %d\n", ret);
} else {
if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) {
int i;
for (i = 0; i < CW2015_RESET_TRIES; i++) {
if (!cw_power_on_reset(cw_bat))
break;
}
}
cw_update_soc(cw_bat);
cw_update_voltage(cw_bat);
cw_update_charge_status(cw_bat);
cw_update_status(cw_bat);
cw_update_time_to_empty(cw_bat);
}
dev_dbg(cw_bat->dev, "charger_attached = %d\n", cw_bat->charger_attached);
dev_dbg(cw_bat->dev, "status = %d\n", cw_bat->status);
dev_dbg(cw_bat->dev, "soc = %d%%\n", cw_bat->soc);
dev_dbg(cw_bat->dev, "voltage = %dmV\n", cw_bat->voltage_mv);
if (cw_bat->battery_changed)
power_supply_changed(cw_bat->rk_bat);
cw_bat->battery_changed = false;
queue_delayed_work(cw_bat->battery_workqueue,
&cw_bat->battery_delay_work,
msecs_to_jiffies(cw_bat->poll_interval_ms));
}
static bool cw_battery_valid_time_to_empty(struct cw_battery *cw_bat)
{
return cw_bat->time_to_empty > 0 &&
cw_bat->time_to_empty < CW2015_MASK_SOC &&
cw_bat->status == POWER_SUPPLY_STATUS_DISCHARGING;
}
static int cw_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct cw_battery *cw_bat;
cw_bat = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = cw_bat->soc;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = cw_bat->status;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = !!cw_bat->voltage_mv;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = cw_bat->voltage_mv * 1000;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
if (cw_battery_valid_time_to_empty(cw_bat))
val->intval = cw_bat->time_to_empty;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
val->intval = cw_bat->charge_count;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
if (cw_bat->battery.charge_full_design_uah > 0)
val->intval = cw_bat->battery.charge_full_design_uah;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (cw_battery_valid_time_to_empty(cw_bat) &&
cw_bat->battery.charge_full_design_uah > 0) {
/* calculate remaining capacity */
val->intval = cw_bat->battery.charge_full_design_uah;
val->intval = val->intval * cw_bat->soc / 100;
/* estimate current based on time to empty */
val->intval = 60 * val->intval / cw_bat->time_to_empty;
} else {
val->intval = 0;
}
break;
default:
break;
}
return 0;
}
static enum power_supply_property cw_battery_properties[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static const struct power_supply_desc cw2015_bat_desc = {
.name = "cw2015-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = cw_battery_properties,
.num_properties = ARRAY_SIZE(cw_battery_properties),
.get_property = cw_battery_get_property,
};
static int cw2015_parse_properties(struct cw_battery *cw_bat)
{
struct device *dev = cw_bat->dev;
int length;
int ret;
length = device_property_count_u8(dev, "cellwise,battery-profile");
if (length < 0) {
dev_warn(cw_bat->dev,
"No battery-profile found, using current flash contents\n");
} else if (length != CW2015_SIZE_BATINFO) {
dev_err(cw_bat->dev, "battery-profile must be %d bytes\n",
CW2015_SIZE_BATINFO);
return -EINVAL;
} else {
cw_bat->bat_profile = devm_kzalloc(dev, length, GFP_KERNEL);
if (!cw_bat->bat_profile)
return -ENOMEM;
ret = device_property_read_u8_array(dev,
"cellwise,battery-profile",
cw_bat->bat_profile,
length);
if (ret)
return ret;
}
ret = device_property_read_u32(dev, "cellwise,monitor-interval-ms",
&cw_bat->poll_interval_ms);
if (ret) {
dev_dbg(cw_bat->dev, "Using default poll interval\n");
cw_bat->poll_interval_ms = CW2015_DEFAULT_POLL_INTERVAL_MS;
}
return 0;
}
static const struct regmap_range regmap_ranges_rd_yes[] = {
regmap_reg_range(CW2015_REG_VERSION, CW2015_REG_VERSION),
regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_CONFIG),
regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE),
regmap_reg_range(CW2015_REG_BATINFO,
CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1),
};
static const struct regmap_access_table regmap_rd_table = {
.yes_ranges = regmap_ranges_rd_yes,
.n_yes_ranges = 4,
};
static const struct regmap_range regmap_ranges_wr_yes[] = {
regmap_reg_range(CW2015_REG_RRT_ALERT, CW2015_REG_CONFIG),
regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE),
regmap_reg_range(CW2015_REG_BATINFO,
CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1),
};
static const struct regmap_access_table regmap_wr_table = {
.yes_ranges = regmap_ranges_wr_yes,
.n_yes_ranges = 3,
};
static const struct regmap_range regmap_ranges_vol_yes[] = {
regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_SOC + 1),
};
static const struct regmap_access_table regmap_vol_table = {
.yes_ranges = regmap_ranges_vol_yes,
.n_yes_ranges = 1,
};
static const struct regmap_config cw2015_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &regmap_rd_table,
.wr_table = &regmap_wr_table,
.volatile_table = &regmap_vol_table,
.max_register = CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1,
};
static int cw_bat_probe(struct i2c_client *client)
{
int ret;
struct cw_battery *cw_bat;
struct power_supply_config psy_cfg = { 0 };
cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL);
if (!cw_bat)
return -ENOMEM;
i2c_set_clientdata(client, cw_bat);
cw_bat->dev = &client->dev;
cw_bat->soc = 1;
ret = cw2015_parse_properties(cw_bat);
if (ret) {
dev_err(cw_bat->dev, "Failed to parse cw2015 properties\n");
return ret;
}
cw_bat->regmap = devm_regmap_init_i2c(client, &cw2015_regmap_config);
if (IS_ERR(cw_bat->regmap)) {
dev_err(cw_bat->dev, "Failed to allocate regmap: %ld\n",
PTR_ERR(cw_bat->regmap));
return PTR_ERR(cw_bat->regmap);
}
ret = cw_init(cw_bat);
if (ret) {
dev_err(cw_bat->dev, "Init failed: %d\n", ret);
return ret;
}
psy_cfg.drv_data = cw_bat;
psy_cfg.fwnode = dev_fwnode(cw_bat->dev);
cw_bat->rk_bat = devm_power_supply_register(&client->dev,
&cw2015_bat_desc,
&psy_cfg);
if (IS_ERR(cw_bat->rk_bat)) {
dev_err(cw_bat->dev, "Failed to register power supply\n");
return PTR_ERR(cw_bat->rk_bat);
}
ret = power_supply_get_battery_info(cw_bat->rk_bat, &cw_bat->battery);
if (ret) {
dev_warn(cw_bat->dev,
"No monitored battery, some properties will be missing\n");
}
cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery");
INIT_DELAYED_WORK(&cw_bat->battery_delay_work, cw_bat_work);
queue_delayed_work(cw_bat->battery_workqueue,
&cw_bat->battery_delay_work, msecs_to_jiffies(10));
return 0;
}
static int __maybe_unused cw_bat_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cw_battery *cw_bat = i2c_get_clientdata(client);
cancel_delayed_work_sync(&cw_bat->battery_delay_work);
return 0;
}
static int __maybe_unused cw_bat_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cw_battery *cw_bat = i2c_get_clientdata(client);
queue_delayed_work(cw_bat->battery_workqueue,
&cw_bat->battery_delay_work, 0);
return 0;
}
static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume);
static int cw_bat_remove(struct i2c_client *client)
{
struct cw_battery *cw_bat = i2c_get_clientdata(client);
cancel_delayed_work_sync(&cw_bat->battery_delay_work);
power_supply_put_battery_info(cw_bat->rk_bat, &cw_bat->battery);
return 0;
}
static const struct i2c_device_id cw_bat_id_table[] = {
{ "cw2015", 0 },
{ }
};
static const struct of_device_id cw2015_of_match[] = {
{ .compatible = "cellwise,cw2015" },
{ }
};
MODULE_DEVICE_TABLE(of, cw2015_of_match);
static struct i2c_driver cw_bat_driver = {
.driver = {
.name = "cw2015",
.of_match_table = cw2015_of_match,
.pm = &cw_bat_pm_ops,
},
.probe_new = cw_bat_probe,
.remove = cw_bat_remove,
.id_table = cw_bat_id_table,
};
module_i2c_driver(cw_bat_driver);
MODULE_AUTHOR("xhc<xhc@rock-chips.com>");
MODULE_AUTHOR("Tobias Schramm <t.schramm@manjaro.org>");
MODULE_DESCRIPTION("cw2015/cw2013 battery driver");
MODULE_LICENSE("GPL");

View File

@ -241,6 +241,7 @@ static int gab_probe(struct platform_device *pdev)
struct power_supply_desc *psy_desc;
struct power_supply_config psy_cfg = {};
struct gab_platform_data *pdata = pdev->dev.platform_data;
enum power_supply_property *properties;
int ret = 0;
int chan;
int index = ARRAY_SIZE(gab_props);
@ -268,16 +269,16 @@ static int gab_probe(struct platform_device *pdev)
* copying the static properties and allocating extra memory for holding
* the extra configurable properties received from platform data.
*/
psy_desc->properties = kcalloc(ARRAY_SIZE(gab_props) +
ARRAY_SIZE(gab_chan_name),
sizeof(*psy_desc->properties),
GFP_KERNEL);
if (!psy_desc->properties) {
properties = kcalloc(ARRAY_SIZE(gab_props) +
ARRAY_SIZE(gab_chan_name),
sizeof(*properties),
GFP_KERNEL);
if (!properties) {
ret = -ENOMEM;
goto first_mem_fail;
}
memcpy(psy_desc->properties, gab_props, sizeof(gab_props));
memcpy(properties, gab_props, sizeof(gab_props));
/*
* getting channel from iio and copying the battery properties
@ -294,13 +295,11 @@ static int gab_probe(struct platform_device *pdev)
int index2;
for (index2 = 0; index2 < index; index2++) {
if (psy_desc->properties[index2] ==
gab_dyn_props[chan])
if (properties[index2] == gab_dyn_props[chan])
break; /* already known */
}
if (index2 == index) /* really new */
psy_desc->properties[index++] =
gab_dyn_props[chan];
properties[index++] = gab_dyn_props[chan];
any = true;
}
}
@ -317,6 +316,7 @@ static int gab_probe(struct platform_device *pdev)
* as come channels may be not be supported by the device.So
* we need to take care of that.
*/
psy_desc->properties = properties;
psy_desc->num_properties = index;
adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
@ -358,7 +358,7 @@ static int gab_probe(struct platform_device *pdev)
iio_channel_release(adc_bat->channel[chan]);
}
second_mem_fail:
kfree(psy_desc->properties);
kfree(properties);
first_mem_fail:
return ret;
}

View File

@ -572,27 +572,14 @@ static void lp8788_setup_adc_channel(struct device *dev,
return;
/* ADC channel for battery voltage */
chan = iio_channel_get(dev, pdata->adc_vbatt);
chan = devm_iio_channel_get(dev, pdata->adc_vbatt);
pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
/* ADC channel for battery temperature */
chan = iio_channel_get(dev, pdata->adc_batt_temp);
chan = devm_iio_channel_get(dev, pdata->adc_batt_temp);
pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
}
static void lp8788_release_adc_channel(struct lp8788_charger *pchg)
{
int i;
for (i = 0; i < LP8788_NUM_CHG_ADC; i++) {
if (!pchg->chan[i])
continue;
iio_channel_release(pchg->chan[i]);
pchg->chan[i] = NULL;
}
}
static ssize_t lp8788_show_charger_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -735,7 +722,6 @@ static int lp8788_charger_remove(struct platform_device *pdev)
flush_work(&pchg->charger_work);
lp8788_irq_unregister(pdev, pchg);
lp8788_psy_unregister(pchg);
lp8788_release_adc_channel(pchg);
return 0;
}

View File

@ -623,9 +623,19 @@ static const struct platform_device_id max14577_charger_id[] = {
};
MODULE_DEVICE_TABLE(platform, max14577_charger_id);
static const struct of_device_id of_max14577_charger_dt_match[] = {
{ .compatible = "maxim,max14577-charger",
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577, },
{ .compatible = "maxim,max77836-charger",
.data = (void *)MAXIM_DEVICE_TYPE_MAX77836, },
{ },
};
MODULE_DEVICE_TABLE(of, of_max14577_charger_dt_match);
static struct platform_driver max14577_charger_driver = {
.driver = {
.name = "max14577-charger",
.of_match_table = of_max14577_charger_dt_match,
},
.probe = max14577_charger_probe,
.remove = max14577_charger_remove,

View File

@ -139,10 +139,9 @@ static void max14656_irq_worker(struct work_struct *work)
u8 buf[REG_TOTAL_NUM];
u8 chg_type;
int ret = 0;
ret = max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID,
REG_TOTAL_NUM, buf);
max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID,
REG_TOTAL_NUM, buf);
if ((buf[MAX14656_STATUS_1] & STATUS1_VB_VALID_MASK) &&
(buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK)) {

View File

@ -126,7 +126,7 @@ static void max17040_get_vcell(struct i2c_client *client)
vcell = max17040_read_reg(client, MAX17040_VCELL);
chip->vcell = vcell;
chip->vcell = (vcell >> 4) * 1250;
}
static void max17040_get_soc(struct i2c_client *client)

View File

@ -87,6 +87,7 @@ static enum power_supply_property max17042_battery_props[] = {
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
};
static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
@ -411,6 +412,13 @@ static int max17042_get_property(struct power_supply *psy,
return -EINVAL;
}
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = regmap_read(map, MAX17042_TTE, &data);
if (ret < 0)
return ret;
val->intval = data * 5625 / 1000;
break;
default:
return -EINVAL;
}

View File

@ -88,7 +88,7 @@ static enum power_supply_property olpc_ac_props[] = {
};
static const struct power_supply_desc olpc_ac_desc = {
.name = "olpc-ac",
.name = "olpc_ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = olpc_ac_props,
.num_properties = ARRAY_SIZE(olpc_ac_props),
@ -605,7 +605,7 @@ static const struct attribute_group *olpc_bat_sysfs_groups[] = {
*********************************************************************/
static struct power_supply_desc olpc_bat_desc = {
.name = "olpc-battery",
.name = "olpc_battery",
.get_property = olpc_bat_get_property,
.use_for_apm = 1,
};

View File

@ -620,10 +620,18 @@ int power_supply_get_battery_info(struct power_supply *psy,
&info->voltage_min_design_uv);
of_property_read_u32(battery_np, "voltage-max-design-microvolt",
&info->voltage_max_design_uv);
of_property_read_u32(battery_np, "trickle-charge-current-microamp",
&info->tricklecharge_current_ua);
of_property_read_u32(battery_np, "precharge-current-microamp",
&info->precharge_current_ua);
of_property_read_u32(battery_np, "precharge-upper-limit-microvolt",
&info->precharge_voltage_max_uv);
of_property_read_u32(battery_np, "charge-term-current-microamp",
&info->charge_term_current_ua);
of_property_read_u32(battery_np, "re-charge-voltage-microvolt",
&info->charge_restart_voltage_uv);
of_property_read_u32(battery_np, "over-voltage-threshold-microvolt",
&info->overvoltage_limit_uv);
of_property_read_u32(battery_np, "constant-charge-current-max-microamp",
&info->constant_charge_current_max_ua);
of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt",

View File

@ -13,6 +13,11 @@ struct power_supply_hwmon {
unsigned long *props;
};
static const char *const ps_temp_label[] = {
"temp",
"ambient temp",
};
static int power_supply_hwmon_in_to_property(u32 attr)
{
switch (attr) {
@ -98,6 +103,39 @@ static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
return type == hwmon_temp && attr == hwmon_temp_label;
}
struct hwmon_type_attr_list {
const u32 *attrs;
size_t n_attrs;
};
static const u32 ps_temp_attrs[] = {
hwmon_temp_input,
hwmon_temp_min, hwmon_temp_max,
hwmon_temp_min_alarm, hwmon_temp_max_alarm,
};
static const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = {
[hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) },
};
static bool power_supply_hwmon_has_input(
const struct power_supply_hwmon *psyhw,
enum hwmon_sensor_types type, int channel)
{
const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type];
size_t i;
for (i = 0; i < attr_list->n_attrs; ++i) {
int prop = power_supply_hwmon_to_property(type,
attr_list->attrs[i], channel);
if (prop >= 0 && test_bit(prop, psyhw->props))
return true;
}
return false;
}
static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
u32 attr)
{
@ -124,9 +162,12 @@ static umode_t power_supply_hwmon_is_visible(const void *data,
const struct power_supply_hwmon *psyhw = data;
int prop;
if (power_supply_hwmon_is_a_label(type, attr))
return 0444;
if (power_supply_hwmon_is_a_label(type, attr)) {
if (power_supply_hwmon_has_input(psyhw, type, channel))
return 0444;
else
return 0;
}
prop = power_supply_hwmon_to_property(type, attr, channel);
if (prop < 0 || !test_bit(prop, psyhw->props))
@ -144,7 +185,20 @@ static int power_supply_hwmon_read_string(struct device *dev,
u32 attr, int channel,
const char **str)
{
*str = channel ? "temp" : "temp ambient";
switch (type) {
case hwmon_temp:
*str = ps_temp_label[channel];
break;
default:
/* unreachable, but see:
* gcc bug #51513 [1] and clang bug #978 [2]
*
* [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513
* [2] https://github.com/ClangBuiltLinux/linux/issues/978
*/
break;
}
return 0;
}
@ -304,7 +358,7 @@ int power_supply_add_hwmon_sysfs(struct power_supply *psy)
goto error;
}
ret = devm_add_action(dev, power_supply_hwmon_bitmap_free,
ret = devm_add_action_or_reset(dev, power_supply_hwmon_bitmap_free,
psyhw->props);
if (ret)
goto error;

View File

@ -18,68 +18,211 @@
#include "power_supply.h"
/*
* This is because the name "current" breaks the device attr macro.
* The "current" word resolves to "(get_current())" so instead of
* "current" "(get_current())" appears in the sysfs.
*
* The source of this definition is the device.h which calls __ATTR
* macro in sysfs.h which calls the __stringify macro.
*
* Only modification that the name is not tried to be resolved
* (as a macro let's say).
*/
#define MAX_PROP_NAME_LEN 30
#define POWER_SUPPLY_ATTR(_name) \
{ \
.attr = { .name = #_name }, \
.show = power_supply_show_property, \
.store = power_supply_store_property, \
struct power_supply_attr {
const char *prop_name;
char attr_name[MAX_PROP_NAME_LEN + 1];
struct device_attribute dev_attr;
const char * const *text_values;
int text_values_len;
};
#define _POWER_SUPPLY_ATTR(_name, _text, _len) \
[POWER_SUPPLY_PROP_ ## _name] = \
{ \
.prop_name = #_name, \
.attr_name = #_name "\0", \
.text_values = _text, \
.text_values_len = _len, \
}
static struct device_attribute power_supply_attrs[];
#define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0)
#define _POWER_SUPPLY_ENUM_ATTR(_name, _text) \
_POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text))
#define POWER_SUPPLY_ENUM_ATTR(_name) \
_POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT)
static const char * const power_supply_type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB",
"USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
"USB_PD", "USB_PD_DRP", "BrickID"
static const char * const POWER_SUPPLY_TYPE_TEXT[] = {
[POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown",
[POWER_SUPPLY_TYPE_BATTERY] = "Battery",
[POWER_SUPPLY_TYPE_UPS] = "UPS",
[POWER_SUPPLY_TYPE_MAINS] = "Mains",
[POWER_SUPPLY_TYPE_USB] = "USB",
[POWER_SUPPLY_TYPE_USB_DCP] = "USB_DCP",
[POWER_SUPPLY_TYPE_USB_CDP] = "USB_CDP",
[POWER_SUPPLY_TYPE_USB_ACA] = "USB_ACA",
[POWER_SUPPLY_TYPE_USB_TYPE_C] = "USB_C",
[POWER_SUPPLY_TYPE_USB_PD] = "USB_PD",
[POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP",
[POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID",
};
static const char * const power_supply_usb_type_text[] = {
"Unknown", "SDP", "DCP", "CDP", "ACA", "C",
"PD", "PD_DRP", "PD_PPS", "BrickID"
static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = {
[POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown",
[POWER_SUPPLY_USB_TYPE_SDP] = "SDP",
[POWER_SUPPLY_USB_TYPE_DCP] = "DCP",
[POWER_SUPPLY_USB_TYPE_CDP] = "CDP",
[POWER_SUPPLY_USB_TYPE_ACA] = "ACA",
[POWER_SUPPLY_USB_TYPE_C] = "C",
[POWER_SUPPLY_USB_TYPE_PD] = "PD",
[POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP",
[POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS",
[POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID",
};
static const char * const power_supply_status_text[] = {
"Unknown", "Charging", "Discharging", "Not charging", "Full"
static const char * const POWER_SUPPLY_STATUS_TEXT[] = {
[POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown",
[POWER_SUPPLY_STATUS_CHARGING] = "Charging",
[POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging",
[POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging",
[POWER_SUPPLY_STATUS_FULL] = "Full",
};
static const char * const power_supply_charge_type_text[] = {
"Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom"
static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = {
[POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown",
[POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A",
[POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle",
[POWER_SUPPLY_CHARGE_TYPE_FAST] = "Fast",
[POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard",
[POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive",
[POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom",
};
static const char * const power_supply_health_text[] = {
"Unknown", "Good", "Overheat", "Dead", "Over voltage",
"Unspecified failure", "Cold", "Watchdog timer expire",
"Safety timer expire", "Over current"
static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
[POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown",
[POWER_SUPPLY_HEALTH_GOOD] = "Good",
[POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat",
[POWER_SUPPLY_HEALTH_DEAD] = "Dead",
[POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage",
[POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure",
[POWER_SUPPLY_HEALTH_COLD] = "Cold",
[POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire",
[POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire",
[POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current",
[POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required",
};
static const char * const power_supply_technology_text[] = {
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
"LiMn"
static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {
[POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown",
[POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH",
[POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion",
[POWER_SUPPLY_TECHNOLOGY_LIPO] = "Li-poly",
[POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe",
[POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd",
[POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn",
};
static const char * const power_supply_capacity_level_text[] = {
"Unknown", "Critical", "Low", "Normal", "High", "Full"
static const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = {
[POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown",
[POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical",
[POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low",
[POWER_SUPPLY_CAPACITY_LEVEL_NORMAL] = "Normal",
[POWER_SUPPLY_CAPACITY_LEVEL_HIGH] = "High",
[POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full",
};
static const char * const power_supply_scope_text[] = {
"Unknown", "System", "Device"
static const char * const POWER_SUPPLY_SCOPE_TEXT[] = {
[POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown",
[POWER_SUPPLY_SCOPE_SYSTEM] = "System",
[POWER_SUPPLY_SCOPE_DEVICE] = "Device",
};
static struct power_supply_attr power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ENUM_ATTR(STATUS),
POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE),
POWER_SUPPLY_ENUM_ATTR(HEALTH),
POWER_SUPPLY_ATTR(PRESENT),
POWER_SUPPLY_ATTR(ONLINE),
POWER_SUPPLY_ATTR(AUTHENTIC),
POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY),
POWER_SUPPLY_ATTR(CYCLE_COUNT),
POWER_SUPPLY_ATTR(VOLTAGE_MAX),
POWER_SUPPLY_ATTR(VOLTAGE_MIN),
POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN),
POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN),
POWER_SUPPLY_ATTR(VOLTAGE_NOW),
POWER_SUPPLY_ATTR(VOLTAGE_AVG),
POWER_SUPPLY_ATTR(VOLTAGE_OCV),
POWER_SUPPLY_ATTR(VOLTAGE_BOOT),
POWER_SUPPLY_ATTR(CURRENT_MAX),
POWER_SUPPLY_ATTR(CURRENT_NOW),
POWER_SUPPLY_ATTR(CURRENT_AVG),
POWER_SUPPLY_ATTR(CURRENT_BOOT),
POWER_SUPPLY_ATTR(POWER_NOW),
POWER_SUPPLY_ATTR(POWER_AVG),
POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN),
POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN),
POWER_SUPPLY_ATTR(CHARGE_FULL),
POWER_SUPPLY_ATTR(CHARGE_EMPTY),
POWER_SUPPLY_ATTR(CHARGE_NOW),
POWER_SUPPLY_ATTR(CHARGE_AVG),
POWER_SUPPLY_ATTR(CHARGE_COUNTER),
POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT),
POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX),
POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE),
POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),
POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),
POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),
POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),
POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN),
POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN),
POWER_SUPPLY_ATTR(ENERGY_FULL),
POWER_SUPPLY_ATTR(ENERGY_EMPTY),
POWER_SUPPLY_ATTR(ENERGY_NOW),
POWER_SUPPLY_ATTR(ENERGY_AVG),
POWER_SUPPLY_ATTR(CAPACITY),
POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN),
POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX),
POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN),
POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL),
POWER_SUPPLY_ATTR(TEMP),
POWER_SUPPLY_ATTR(TEMP_MAX),
POWER_SUPPLY_ATTR(TEMP_MIN),
POWER_SUPPLY_ATTR(TEMP_ALERT_MIN),
POWER_SUPPLY_ATTR(TEMP_ALERT_MAX),
POWER_SUPPLY_ATTR(TEMP_AMBIENT),
POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN),
POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX),
POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW),
POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG),
POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW),
POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG),
POWER_SUPPLY_ENUM_ATTR(TYPE),
POWER_SUPPLY_ATTR(USB_TYPE),
POWER_SUPPLY_ENUM_ATTR(SCOPE),
POWER_SUPPLY_ATTR(PRECHARGE_CURRENT),
POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT),
POWER_SUPPLY_ATTR(CALIBRATE),
POWER_SUPPLY_ATTR(MANUFACTURE_YEAR),
POWER_SUPPLY_ATTR(MANUFACTURE_MONTH),
POWER_SUPPLY_ATTR(MANUFACTURE_DAY),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(MODEL_NAME),
POWER_SUPPLY_ATTR(MANUFACTURER),
POWER_SUPPLY_ATTR(SERIAL_NUMBER),
};
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
static struct power_supply_attr *to_ps_attr(struct device_attribute *attr)
{
return container_of(attr, struct power_supply_attr, dev_attr);
}
static enum power_supply_property dev_attr_psp(struct device_attribute *attr)
{
return to_ps_attr(attr) - power_supply_attrs;
}
static ssize_t power_supply_show_usb_type(struct device *dev,
enum power_supply_usb_type *usb_types,
ssize_t num_usb_types,
const struct power_supply_desc *desc,
union power_supply_propval *value,
char *buf)
{
@ -88,16 +231,16 @@ static ssize_t power_supply_show_usb_type(struct device *dev,
bool match = false;
int i;
for (i = 0; i < num_usb_types; ++i) {
usb_type = usb_types[i];
for (i = 0; i < desc->num_usb_types; ++i) {
usb_type = desc->usb_types[i];
if (value->intval == usb_type) {
count += sprintf(buf + count, "[%s] ",
power_supply_usb_type_text[usb_type]);
POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
match = true;
} else {
count += sprintf(buf + count, "%s ",
power_supply_usb_type_text[usb_type]);
POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
}
}
@ -117,7 +260,8 @@ static ssize_t power_supply_show_property(struct device *dev,
char *buf) {
ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev);
enum power_supply_property psp = attr - power_supply_attrs;
struct power_supply_attr *ps_attr = to_ps_attr(attr);
enum power_supply_property psp = dev_attr_psp(attr);
union power_supply_propval value;
if (psp == POWER_SUPPLY_PROP_TYPE) {
@ -137,39 +281,15 @@ static ssize_t power_supply_show_property(struct device *dev,
}
}
if (ps_attr->text_values_len > 0 &&
value.intval < ps_attr->text_values_len && value.intval >= 0) {
return sprintf(buf, "%s\n", ps_attr->text_values[value.intval]);
}
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = sprintf(buf, "%s\n",
power_supply_status_text[value.intval]);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = sprintf(buf, "%s\n",
power_supply_charge_type_text[value.intval]);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = sprintf(buf, "%s\n",
power_supply_health_text[value.intval]);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
ret = sprintf(buf, "%s\n",
power_supply_technology_text[value.intval]);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
ret = sprintf(buf, "%s\n",
power_supply_capacity_level_text[value.intval]);
break;
case POWER_SUPPLY_PROP_TYPE:
ret = sprintf(buf, "%s\n",
power_supply_type_text[value.intval]);
break;
case POWER_SUPPLY_PROP_USB_TYPE:
ret = power_supply_show_usb_type(dev, psy->desc->usb_types,
psy->desc->num_usb_types,
&value, buf);
break;
case POWER_SUPPLY_PROP_SCOPE:
ret = sprintf(buf, "%s\n",
power_supply_scope_text[value.intval]);
ret = power_supply_show_usb_type(dev, psy->desc,
&value, buf);
break;
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = sprintf(buf, "%s\n", value.strval);
@ -186,30 +306,14 @@ static ssize_t power_supply_store_property(struct device *dev,
const char *buf, size_t count) {
ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev);
enum power_supply_property psp = attr - power_supply_attrs;
struct power_supply_attr *ps_attr = to_ps_attr(attr);
enum power_supply_property psp = dev_attr_psp(attr);
union power_supply_propval value;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = sysfs_match_string(power_supply_status_text, buf);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = sysfs_match_string(power_supply_charge_type_text, buf);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = sysfs_match_string(power_supply_health_text, buf);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
ret = sysfs_match_string(power_supply_technology_text, buf);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
ret = sysfs_match_string(power_supply_capacity_level_text, buf);
break;
case POWER_SUPPLY_PROP_SCOPE:
ret = sysfs_match_string(power_supply_scope_text, buf);
break;
default:
ret = -EINVAL;
ret = -EINVAL;
if (ps_attr->text_values_len > 0) {
ret = __sysfs_match_string(ps_attr->text_values,
ps_attr->text_values_len, buf);
}
/*
@ -235,86 +339,6 @@ static ssize_t power_supply_store_property(struct device *dev,
return count;
}
/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ATTR(status),
POWER_SUPPLY_ATTR(charge_type),
POWER_SUPPLY_ATTR(health),
POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online),
POWER_SUPPLY_ATTR(authentic),
POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(cycle_count),
POWER_SUPPLY_ATTR(voltage_max),
POWER_SUPPLY_ATTR(voltage_min),
POWER_SUPPLY_ATTR(voltage_max_design),
POWER_SUPPLY_ATTR(voltage_min_design),
POWER_SUPPLY_ATTR(voltage_now),
POWER_SUPPLY_ATTR(voltage_avg),
POWER_SUPPLY_ATTR(voltage_ocv),
POWER_SUPPLY_ATTR(voltage_boot),
POWER_SUPPLY_ATTR(current_max),
POWER_SUPPLY_ATTR(current_now),
POWER_SUPPLY_ATTR(current_avg),
POWER_SUPPLY_ATTR(current_boot),
POWER_SUPPLY_ATTR(power_now),
POWER_SUPPLY_ATTR(power_avg),
POWER_SUPPLY_ATTR(charge_full_design),
POWER_SUPPLY_ATTR(charge_empty_design),
POWER_SUPPLY_ATTR(charge_full),
POWER_SUPPLY_ATTR(charge_empty),
POWER_SUPPLY_ATTR(charge_now),
POWER_SUPPLY_ATTR(charge_avg),
POWER_SUPPLY_ATTR(charge_counter),
POWER_SUPPLY_ATTR(constant_charge_current),
POWER_SUPPLY_ATTR(constant_charge_current_max),
POWER_SUPPLY_ATTR(constant_charge_voltage),
POWER_SUPPLY_ATTR(constant_charge_voltage_max),
POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max),
POWER_SUPPLY_ATTR(charge_control_start_threshold),
POWER_SUPPLY_ATTR(charge_control_end_threshold),
POWER_SUPPLY_ATTR(input_current_limit),
POWER_SUPPLY_ATTR(input_voltage_limit),
POWER_SUPPLY_ATTR(input_power_limit),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full),
POWER_SUPPLY_ATTR(energy_empty),
POWER_SUPPLY_ATTR(energy_now),
POWER_SUPPLY_ATTR(energy_avg),
POWER_SUPPLY_ATTR(capacity),
POWER_SUPPLY_ATTR(capacity_alert_min),
POWER_SUPPLY_ATTR(capacity_alert_max),
POWER_SUPPLY_ATTR(capacity_level),
POWER_SUPPLY_ATTR(temp),
POWER_SUPPLY_ATTR(temp_max),
POWER_SUPPLY_ATTR(temp_min),
POWER_SUPPLY_ATTR(temp_alert_min),
POWER_SUPPLY_ATTR(temp_alert_max),
POWER_SUPPLY_ATTR(temp_ambient),
POWER_SUPPLY_ATTR(temp_ambient_alert_min),
POWER_SUPPLY_ATTR(temp_ambient_alert_max),
POWER_SUPPLY_ATTR(time_to_empty_now),
POWER_SUPPLY_ATTR(time_to_empty_avg),
POWER_SUPPLY_ATTR(time_to_full_now),
POWER_SUPPLY_ATTR(time_to_full_avg),
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(usb_type),
POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(precharge_current),
POWER_SUPPLY_ATTR(charge_term_current),
POWER_SUPPLY_ATTR(calibrate),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
POWER_SUPPLY_ATTR(serial_number),
};
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
static umode_t power_supply_attr_is_visible(struct kobject *kobj,
struct attribute *attr,
int attrno)
@ -324,6 +348,9 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
int i;
if (!power_supply_attrs[attrno].prop_name)
return 0;
if (attrno == POWER_SUPPLY_PROP_TYPE)
return mode;
@ -352,31 +379,69 @@ static const struct attribute_group *power_supply_attr_groups[] = {
NULL,
};
static void str_to_lower(char *str)
{
while (*str) {
*str = tolower(*str);
str++;
}
}
void power_supply_init_attrs(struct device_type *dev_type)
{
int i;
dev_type->groups = power_supply_attr_groups;
for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
__power_supply_attrs[i] = &power_supply_attrs[i].attr;
for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) {
struct device_attribute *attr;
if (!power_supply_attrs[i].prop_name) {
pr_warn("%s: Property %d skipped because is is missing from power_supply_attrs\n",
__func__, i);
sprintf(power_supply_attrs[i].attr_name, "_err_%d", i);
} else {
str_to_lower(power_supply_attrs[i].attr_name);
}
attr = &power_supply_attrs[i].dev_attr;
attr->attr.name = power_supply_attrs[i].attr_name;
attr->show = power_supply_show_property;
attr->store = power_supply_store_property;
__power_supply_attrs[i] = &attr->attr;
}
}
static char *kstruprdup(const char *str, gfp_t gfp)
static int add_prop_uevent(struct device *dev, struct kobj_uevent_env *env,
enum power_supply_property prop, char *prop_buf)
{
char *ret, *ustr;
int ret = 0;
struct power_supply_attr *pwr_attr;
struct device_attribute *dev_attr;
char *line;
ustr = ret = kmalloc(strlen(str) + 1, gfp);
pwr_attr = &power_supply_attrs[prop];
dev_attr = &pwr_attr->dev_attr;
if (!ret)
return NULL;
ret = power_supply_show_property(dev, dev_attr, prop_buf);
if (ret == -ENODEV || ret == -ENODATA) {
/*
* When a battery is absent, we expect -ENODEV. Don't abort;
* send the uevent with at least the the PRESENT=0 property
*/
return 0;
}
while (*str)
*ustr++ = toupper(*str++);
if (ret < 0)
return ret;
*ustr = 0;
line = strchr(prop_buf, '\n');
if (line)
*line = 0;
return ret;
return add_uevent_var(env, "POWER_SUPPLY_%s=%s",
pwr_attr->prop_name, prop_buf);
}
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
@ -384,7 +449,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
struct power_supply *psy = dev_get_drvdata(dev);
int ret = 0, j;
char *prop_buf;
char *attrname;
if (!psy || !psy->desc) {
dev_dbg(dev, "No power supply yet\n");
@ -399,35 +463,13 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
if (!prop_buf)
return -ENOMEM;
ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf);
if (ret)
goto out;
for (j = 0; j < psy->desc->num_properties; j++) {
struct device_attribute *attr;
char *line;
attr = &power_supply_attrs[psy->desc->properties[j]];
ret = power_supply_show_property(dev, attr, prop_buf);
if (ret == -ENODEV || ret == -ENODATA) {
/* When a battery is absent, we expect -ENODEV. Don't abort;
send the uevent with at least the the PRESENT=0 property */
ret = 0;
continue;
}
if (ret < 0)
goto out;
line = strchr(prop_buf, '\n');
if (line)
*line = 0;
attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
if (!attrname) {
ret = -ENOMEM;
goto out;
}
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
kfree(attrname);
ret = add_prop_uevent(dev, env, psy->desc->properties[j],
prop_buf);
if (ret)
goto out;
}

View File

@ -14,7 +14,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/of_device.h>
#include <linux/power/sbs-battery.h>
#include <linux/power_supply.h>
@ -23,9 +23,12 @@
enum {
REG_MANUFACTURER_DATA,
REG_BATTERY_MODE,
REG_TEMPERATURE,
REG_VOLTAGE,
REG_CURRENT,
REG_CURRENT_NOW,
REG_CURRENT_AVG,
REG_MAX_ERR,
REG_CAPACITY,
REG_TIME_TO_EMPTY,
REG_TIME_TO_FULL,
@ -41,10 +44,15 @@ enum {
REG_DESIGN_CAPACITY_CHARGE,
REG_DESIGN_VOLTAGE_MIN,
REG_DESIGN_VOLTAGE_MAX,
REG_CHEMISTRY,
REG_MANUFACTURER,
REG_MODEL_NAME,
REG_CHARGE_CURRENT,
REG_CHARGE_VOLTAGE,
};
#define REG_ADDR_MANUFACTURE_DATE 0x1B
/* Battery Mode defines */
#define BATTERY_MODE_OFFSET 0x03
#define BATTERY_MODE_CAPACITY_MASK BIT(15)
@ -52,6 +60,7 @@ enum sbs_capacity_mode {
CAPACITY_MODE_AMPS = 0,
CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK
};
#define BATTERY_MODE_CHARGER_MASK (1<<14)
/* manufacturer access defines */
#define MANUFACTURER_ACCESS_STATUS 0x0006
@ -79,12 +88,18 @@ static const struct chip_data {
} sbs_data[] = {
[REG_MANUFACTURER_DATA] =
SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
[REG_BATTERY_MODE] =
SBS_DATA(-1, 0x03, 0, 65535),
[REG_TEMPERATURE] =
SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
[REG_VOLTAGE] =
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
[REG_CURRENT] =
[REG_CURRENT_NOW] =
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
[REG_CURRENT_AVG] =
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_AVG, 0x0B, -32768, 32767),
[REG_MAX_ERR] =
SBS_DATA(POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, 0x0c, 0, 100),
[REG_CAPACITY] =
SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
[REG_REMAINING_CAPACITY] =
@ -99,6 +114,10 @@ static const struct chip_data {
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
[REG_TIME_TO_FULL] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
[REG_CHARGE_CURRENT] =
SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535),
[REG_CHARGE_VOLTAGE] =
SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 0x15, 0, 65535),
[REG_STATUS] =
SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
[REG_CAPACITY_LEVEL] =
@ -119,10 +138,12 @@ static const struct chip_data {
[REG_MANUFACTURER] =
SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535),
[REG_MODEL_NAME] =
SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535)
SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535),
[REG_CHEMISTRY] =
SBS_DATA(POWER_SUPPLY_PROP_TECHNOLOGY, 0x22, 0, 65535)
};
static enum power_supply_property sbs_properties[] = {
static const enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_HEALTH,
@ -131,7 +152,9 @@ static enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
@ -144,13 +167,18 @@ static enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME
};
/* Supports special manufacturer commands from TI BQ20Z75 IC. */
#define SBS_FLAGS_TI_BQ20Z75 BIT(0)
/* Supports special manufacturer commands from TI BQ20Z65 and BQ20Z75 IC. */
#define SBS_FLAGS_TI_BQ20ZX5 BIT(0)
struct sbs_info {
struct i2c_client *client;
@ -158,6 +186,7 @@ struct sbs_info {
bool is_present;
struct gpio_desc *gpio_detect;
bool enable_detection;
bool charger_broadcasts;
int last_state;
int poll_time;
u32 i2c_retry_count;
@ -169,8 +198,48 @@ struct sbs_info {
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
static char chemistry[I2C_SMBUS_BLOCK_MAX + 1];
static bool force_load;
static int sbs_read_word_data(struct i2c_client *client, u8 address);
static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value);
static void sbs_disable_charger_broadcasts(struct sbs_info *chip)
{
int val = sbs_read_word_data(chip->client, BATTERY_MODE_OFFSET);
if (val < 0)
goto exit;
val |= BATTERY_MODE_CHARGER_MASK;
val = sbs_write_word_data(chip->client, BATTERY_MODE_OFFSET, val);
exit:
if (val < 0)
dev_err(&chip->client->dev,
"Failed to disable charger broadcasting: %d\n", val);
else
dev_dbg(&chip->client->dev, "%s\n", __func__);
}
static int sbs_update_presence(struct sbs_info *chip, bool is_present)
{
if (chip->is_present == is_present)
return 0;
if (!is_present) {
chip->is_present = false;
return 0;
}
if (!chip->is_present && is_present && !chip->charger_broadcasts)
sbs_disable_charger_broadcasts(chip);
chip->is_present = true;
return 0;
}
static int sbs_read_word_data(struct i2c_client *client, u8 address)
{
struct sbs_info *chip = i2c_get_clientdata(client);
@ -288,15 +357,15 @@ static int sbs_status_correct(struct i2c_client *client, int *intval)
{
int ret;
ret = sbs_read_word_data(client, sbs_data[REG_CURRENT].addr);
ret = sbs_read_word_data(client, sbs_data[REG_CURRENT_NOW].addr);
if (ret < 0)
return ret;
ret = (s16)ret;
/* Not drawing current means full (cannot be not charging) */
if (ret == 0)
*intval = POWER_SUPPLY_STATUS_FULL;
/* Not drawing current -> not charging (i.e. idle) */
if (*intval != POWER_SUPPLY_STATUS_FULL && ret == 0)
*intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
if (*intval == POWER_SUPPLY_STATUS_FULL) {
/* Drawing or providing current when full */
@ -309,6 +378,17 @@ static int sbs_status_correct(struct i2c_client *client, int *intval)
return 0;
}
static bool sbs_bat_needs_calibration(struct i2c_client *client)
{
int ret;
ret = sbs_read_word_data(client, sbs_data[REG_BATTERY_MODE].addr);
if (ret < 0)
return false;
return !!(ret & BIT(7));
}
static int sbs_get_battery_presence_and_health(
struct i2c_client *client, enum power_supply_property psp,
union power_supply_propval *val)
@ -328,9 +408,14 @@ static int sbs_get_battery_presence_and_health(
if (psp == POWER_SUPPLY_PROP_PRESENT)
val->intval = 1; /* battery present */
else /* POWER_SUPPLY_PROP_HEALTH */
/* SBS spec doesn't have a general health command. */
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
else { /* POWER_SUPPLY_PROP_HEALTH */
if (sbs_bat_needs_calibration(client)) {
val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED;
} else {
/* SBS spec doesn't have a general health command. */
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
}
}
return 0;
}
@ -384,6 +469,8 @@ static int sbs_get_ti_battery_presence_and_health(
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
else if (ret == 0x0C)
val->intval = POWER_SUPPLY_HEALTH_DEAD;
else if (sbs_bat_needs_calibration(client))
val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED;
else
val->intval = POWER_SUPPLY_HEALTH_GOOD;
}
@ -492,7 +579,10 @@ static void sbs_unit_adjustment(struct i2c_client *client,
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_CHARGE_NOW:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval *= BASE_UNIT_CONVERSION;
@ -602,6 +692,70 @@ static int sbs_get_property_index(struct i2c_client *client,
return -EINVAL;
}
static int sbs_get_chemistry(struct i2c_client *client,
union power_supply_propval *val)
{
enum power_supply_property psp = POWER_SUPPLY_PROP_TECHNOLOGY;
int ret;
ret = sbs_get_property_index(client, psp);
if (ret < 0)
return ret;
ret = sbs_get_battery_string_property(client, ret, psp,
chemistry);
if (ret < 0)
return ret;
if (!strncasecmp(chemistry, "LION", 4))
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
else if (!strncasecmp(chemistry, "LiP", 3))
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
else if (!strncasecmp(chemistry, "NiCd", 4))
val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
else if (!strncasecmp(chemistry, "NiMH", 4))
val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
else
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
if (val->intval == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
dev_warn(&client->dev, "Unknown chemistry: %s\n", chemistry);
return 0;
}
static int sbs_get_battery_manufacture_date(struct i2c_client *client,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret;
u16 day, month, year;
ret = sbs_read_word_data(client, REG_ADDR_MANUFACTURE_DATE);
if (ret < 0)
return ret;
day = ret & GENMASK(4, 0);
month = (ret & GENMASK(8, 5)) >> 5;
year = ((ret & GENMASK(15, 9)) >> 9) + 1980;
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
val->intval = year;
break;
case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
val->intval = month;
break;
case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
val->intval = day;
break;
default:
return -EINVAL;
}
return 0;
}
static int sbs_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@ -616,7 +770,7 @@ static int sbs_get_property(struct power_supply *psy,
return ret;
if (psp == POWER_SUPPLY_PROP_PRESENT) {
val->intval = ret;
chip->is_present = val->intval;
sbs_update_presence(chip, ret);
return 0;
}
if (ret == 0)
@ -626,7 +780,7 @@ static int sbs_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_HEALTH:
if (chip->flags & SBS_FLAGS_TI_BQ20Z75)
if (chip->flags & SBS_FLAGS_TI_BQ20ZX5)
ret = sbs_get_ti_battery_presence_and_health(client,
psp, val);
else
@ -639,7 +793,10 @@ static int sbs_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
ret = sbs_get_chemistry(client, val);
if (ret < 0)
break;
goto done; /* don't trigger power_supply_changed()! */
case POWER_SUPPLY_PROP_ENERGY_NOW:
@ -670,12 +827,16 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CYCLE_COUNT:
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_CAPACITY:
case POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN:
ret = sbs_get_property_index(client, psp);
if (ret < 0)
break;
@ -703,6 +864,12 @@ static int sbs_get_property(struct power_supply *psy,
val->strval = manufacturer;
break;
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
ret = sbs_get_battery_manufacture_date(client, psp, val);
break;
default:
dev_err(&client->dev,
"%s: INVALID property\n", __func__);
@ -714,7 +881,7 @@ static int sbs_get_property(struct power_supply *psy,
if (!chip->gpio_detect &&
chip->is_present != (ret >= 0)) {
chip->is_present = (ret >= 0);
sbs_update_presence(chip, (ret >= 0));
power_supply_changed(chip->power_supply);
}
@ -745,7 +912,7 @@ static void sbs_supply_changed(struct sbs_info *chip)
ret = gpiod_get_value_cansleep(chip->gpio_detect);
if (ret < 0)
return;
chip->is_present = ret;
sbs_update_presence(chip, ret);
power_supply_changed(battery);
}
@ -815,8 +982,7 @@ static const struct power_supply_desc sbs_default_desc = {
.external_power_changed = sbs_external_power_changed,
};
static int sbs_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int sbs_probe(struct i2c_client *client)
{
struct sbs_info *chip;
struct power_supply_desc *sbs_desc;
@ -839,7 +1005,7 @@ static int sbs_probe(struct i2c_client *client,
if (!chip)
return -ENOMEM;
chip->flags = (u32)(uintptr_t)of_device_get_match_data(&client->dev);
chip->flags = (u32)(uintptr_t)device_get_match_data(&client->dev);
chip->client = client;
chip->enable_detection = false;
psy_cfg.of_node = client->dev.of_node;
@ -850,13 +1016,13 @@ static int sbs_probe(struct i2c_client *client,
/* use pdata if available, fall back to DT properties,
* or hardcoded defaults if not
*/
rc = of_property_read_u32(client->dev.of_node, "sbs,i2c-retry-count",
&chip->i2c_retry_count);
rc = device_property_read_u32(&client->dev, "sbs,i2c-retry-count",
&chip->i2c_retry_count);
if (rc)
chip->i2c_retry_count = 0;
rc = of_property_read_u32(client->dev.of_node, "sbs,poll-retry-count",
&chip->poll_retry_count);
rc = device_property_read_u32(&client->dev, "sbs,poll-retry-count",
&chip->poll_retry_count);
if (rc)
chip->poll_retry_count = 0;
@ -866,6 +1032,9 @@ static int sbs_probe(struct i2c_client *client,
}
chip->i2c_retry_count = chip->i2c_retry_count + 1;
chip->charger_broadcasts = !device_property_read_bool(&client->dev,
"sbs,disable-charger-broadcasts");
chip->gpio_detect = devm_gpiod_get_optional(&client->dev,
"sbs,battery-detect", GPIOD_IN);
if (IS_ERR(chip->gpio_detect)) {
@ -950,7 +1119,7 @@ static int sbs_suspend(struct device *dev)
if (chip->poll_time > 0)
cancel_delayed_work_sync(&chip->work);
if (chip->flags & SBS_FLAGS_TI_BQ20Z75) {
if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) {
/* Write to manufacturer access with sleep command. */
ret = sbs_write_word_data(client,
sbs_data[REG_MANUFACTURER_DATA].addr,
@ -970,6 +1139,7 @@ static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
#endif
static const struct i2c_device_id sbs_id[] = {
{ "bq20z65", 0 },
{ "bq20z75", 0 },
{ "sbs-battery", 1 },
{}
@ -978,16 +1148,20 @@ MODULE_DEVICE_TABLE(i2c, sbs_id);
static const struct of_device_id sbs_dt_ids[] = {
{ .compatible = "sbs,sbs-battery" },
{
.compatible = "ti,bq20z65",
.data = (void *)SBS_FLAGS_TI_BQ20ZX5,
},
{
.compatible = "ti,bq20z75",
.data = (void *)SBS_FLAGS_TI_BQ20Z75,
.data = (void *)SBS_FLAGS_TI_BQ20ZX5,
},
{ }
};
MODULE_DEVICE_TABLE(of, sbs_dt_ids);
static struct i2c_driver sbs_battery_driver = {
.probe = sbs_probe,
.probe_new = sbs_probe,
.remove = sbs_remove,
.alert = sbs_alert,
.id_table = sbs_id,

View File

@ -42,6 +42,8 @@
#define SC27XX_FGU_USER_AREA_SET 0xa0
#define SC27XX_FGU_USER_AREA_CLEAR 0xa4
#define SC27XX_FGU_USER_AREA_STATUS 0xa8
#define SC27XX_FGU_VOLTAGE_BUF 0xd0
#define SC27XX_FGU_CURRENT_BUF 0xf0
#define SC27XX_WRITE_SELCLB_EN BIT(0)
#define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0)
@ -82,6 +84,7 @@
* @init_clbcnt: the initial coulomb counter
* @max_volt: the maximum constant input voltage in millivolt
* @min_volt: the minimum drained battery voltage in microvolt
* @boot_volt: the voltage measured during boot in microvolt
* @table_len: the capacity table length
* @resist_table_len: the resistance table length
* @cur_1000ma_adc: ADC value corresponding to 1000 mA
@ -107,6 +110,7 @@ struct sc27xx_fgu_data {
int init_clbcnt;
int max_volt;
int min_volt;
int boot_volt;
int table_len;
int resist_table_len;
int cur_1000ma_adc;
@ -319,6 +323,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
volt = sc27xx_fgu_adc_to_voltage(data, volt);
ocv = volt * 1000 - oci * data->internal_resist;
data->boot_volt = ocv;
/*
* Parse the capacity table to look up the correct capacity percent
@ -376,6 +381,44 @@ static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
return 0;
}
static int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val)
{
int ret;
u32 vol;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF,
&vol);
if (ret)
return ret;
/*
* It is ADC values reading from registers which need to convert to
* corresponding voltage values.
*/
*val = sc27xx_fgu_adc_to_voltage(data, vol);
return 0;
}
static int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val)
{
int ret;
u32 cur;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF,
&cur);
if (ret)
return ret;
/*
* It is ADC values reading from registers which need to convert to
* corresponding current values.
*/
*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
return 0;
}
static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
{
int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
@ -577,7 +620,7 @@ static int sc27xx_fgu_get_property(struct power_supply *psy,
val->intval = value;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
ret = sc27xx_fgu_get_vbat_vol(data, &value);
if (ret)
goto error;
@ -601,7 +644,6 @@ static int sc27xx_fgu_get_property(struct power_supply *psy,
val->intval = value;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = sc27xx_fgu_get_current(data, &value);
if (ret)
@ -625,6 +667,26 @@ static int sc27xx_fgu_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = sc27xx_fgu_get_vol_now(data, &value);
if (ret)
goto error;
val->intval = value * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = sc27xx_fgu_get_cur_now(data, &value);
if (ret)
goto error;
val->intval = value * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
val->intval = data->boot_volt;
break;
default:
ret = -EINVAL;
break;
@ -656,6 +718,11 @@ static int sc27xx_fgu_set_property(struct power_supply *psy,
ret = 0;
break;
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
data->total_cap = val->intval / 1000;
ret = 0;
break;
default:
ret = -EINVAL;
}
@ -676,7 +743,8 @@ static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
return psp == POWER_SUPPLY_PROP_CAPACITY ||
psp == POWER_SUPPLY_PROP_CALIBRATE;
psp == POWER_SUPPLY_PROP_CALIBRATE ||
psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
}
static enum power_supply_property sc27xx_fgu_props[] = {
@ -688,6 +756,8 @@ static enum power_supply_property sc27xx_fgu_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
@ -705,6 +775,7 @@ static const struct power_supply_desc sc27xx_fgu_desc = {
.set_property = sc27xx_fgu_set_property,
.external_power_changed = sc27xx_fgu_external_power_changed,
.property_is_writeable = sc27xx_fgu_property_is_writeable,
.no_thermal = true,
};
static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)

View File

@ -8,6 +8,7 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
@ -708,6 +709,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
bool handled = false;
int ret;
/* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */
usleep_range(25000, 35000);
ret = regmap_read(smb->regmap, STAT_C, &stat_c);
if (ret < 0) {
dev_warn(smb->dev, "reading STAT_C failed\n");
@ -1138,6 +1142,7 @@ static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case IRQSTAT_A:
case IRQSTAT_C:
case IRQSTAT_D:
case IRQSTAT_E:
case IRQSTAT_F:
case STAT_A:

View File

@ -29,7 +29,7 @@
#include <linux/uidgid.h>
#define UEVENT_HELPER_PATH_LEN 256
#define UEVENT_NUM_ENVP 32 /* number of env pointers */
#define UEVENT_NUM_ENVP 64 /* number of env pointers */
#define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
#ifdef CONFIG_UEVENT_HELPER

View File

@ -61,6 +61,7 @@ enum {
POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
POWER_SUPPLY_HEALTH_OVERCURRENT,
POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED,
};
enum {
@ -139,6 +140,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MAX,
@ -158,6 +160,9 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CALIBRATE,
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
@ -223,9 +228,9 @@ struct power_supply_config {
struct power_supply_desc {
const char *name;
enum power_supply_type type;
enum power_supply_usb_type *usb_types;
const enum power_supply_usb_type *usb_types;
size_t num_usb_types;
enum power_supply_property *properties;
const enum power_supply_property *properties;
size_t num_properties;
/*
@ -346,8 +351,12 @@ struct power_supply_battery_info {
int charge_full_design_uah; /* microAmp-hours */
int voltage_min_design_uv; /* microVolts */
int voltage_max_design_uv; /* microVolts */
int tricklecharge_current_ua; /* microAmps */
int precharge_current_ua; /* microAmps */
int precharge_voltage_max_uv; /* microVolts */
int charge_term_current_ua; /* microAmps */
int charge_restart_voltage_uv; /* microVolts */
int overvoltage_limit_uv; /* microVolts */
int constant_charge_current_max_ua; /* microAmps */
int constant_charge_voltage_max_uv; /* microVolts */
int factory_internal_resistance_uohm; /* microOhms */