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:
commit
3a2a875174
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
...
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
91
Documentation/devicetree/bindings/power/supply/bq27xxx.yaml
Normal file
91
Documentation/devicetree/bindings/power/supply/bq27xxx.yaml
Normal 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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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.
|
||||
|
|
155
Documentation/devicetree/bindings/power/supply/rohm,bd99954.yaml
Normal file
155
Documentation/devicetree/bindings/power/supply/rohm,bd99954.yaml
Normal 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>;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
}
|
|
@ -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,.*":
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
233
drivers/power/reset/oxnas-restart.c
Normal file
233
drivers/power/reset/oxnas-restart.c
Normal 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);
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(¤t_limit_ranges[0],
|
||||
ARRAY_SIZE(current_limit_ranges), sel,
|
||||
ma);
|
||||
|
||||
ret = linear_range_get_value_array(¤t_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,
|
||||
®, &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, ®, &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(¤t_limit_ranges[0],
|
||||
ARRAY_SIZE(current_limit_ranges), ma,
|
||||
®, &found);
|
||||
ret = linear_range_get_selector_low_array(current_limit_ranges,
|
||||
ARRAY_SIZE(current_limit_ranges),
|
||||
ma, ®, &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:
|
||||
|
|
1142
drivers/power/supply/bd99954-charger.c
Normal file
1142
drivers/power/supply/bd99954-charger.c
Normal file
File diff suppressed because it is too large
Load Diff
1075
drivers/power/supply/bd99954-charger.h
Normal file
1075
drivers/power/supply/bd99954-charger.h
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
750
drivers/power/supply/cw2015_battery.c
Normal file
750
drivers/power/supply/cw2015_battery.c
Normal 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, ®_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, ®_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, ®_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, ®_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, ®_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 = ®map_rd_table,
|
||||
.wr_table = ®map_wr_table,
|
||||
.volatile_table = ®map_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");
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue
Block a user