mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 15:41:52 +02:00
power supply and reset changes for the 6.16 series
* power-supply core - power: supply: support charge_types in extensions * power-supply drivers - new driver for Pegatron Chagall battery - new driver for Maxim MAX8971 charger - new driver for Huawei Matebook E Go - bq27xxx: retry failed I2C transmissions - bq24190: add BQ24193 support - misc. small cleanups and fixes * reset drivers - new driver for Toradex SMARC Embedded Controller - reboot-mode: add support for modes containing / in DT - atmel,at91sam9260-reset: support sama7d65 - syscon-reboot: add Google GS101 support - misc. small cleanups and fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmg0RIsACgkQ2O7X88g7 +ppO3RAAhvaAnwBevIldvayaL/8upc4weEm13JKfZ2rrbmKCzFvaIIbi9uJxcp7U utmO2T7JKXvh6HzzW2MMTx/CEDfHZJxcfjEzdt3ajOXKq6KcFQ/f0psk2ZzY/Tej /L7dV3ps4RZRDDG+q9v3EsE5/A3KiFyLekzGhQovgwBnSLg8JVoPYc/gXYVq1R8L bx8If8SkpJn99gLBhuHEkWVBSFvpXFCOu75wAiSeULBdc/KefP/x5TPEi5CdIHti zLAukmr8egVI3CxjHb5lIgfh0fbfpwgCjyZLAoW8IUZGykoorwbKFy+jTdUPJqc4 Q/hRBegSKF15TcxXDROYto+FQpTX20BGIf+lasqXKePAk57f6+QjMCW6MWr7JmId 0HVN82Q4DNw0Au0dEWAe3WLY4BgABgx0tbsdhUqchgW5APaQTJ452JiPvSAFwygh m2MXKWwUHADfu6j+Mqk7PyNw4pUq9Xk3vQxDyXzrrd7w5AFk14Ttr/L1jNTJwEA3 cDNa10Wdu6zToIzoGDy5uE4atwQfjVDx1JQ53clIueSM2f92lPjCTCZSBl0XuCAv Ic/LvuCeOQp4VbYWuB7ygTzSBA6M0hcUGgUmGUxy/sg6EPQiv93gKk9ET7dI6sF+ WDX7hS7kGqAathU1TFY3GWnu5RQhSFzhhJ6G3gPuKTEgsEYDg1w= =+qUp -----END PGP SIGNATURE----- Merge tag 'for-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: "Power-supply core: - support charge_types in extensions Power-supply drivers: - new driver for Pegatron Chagall battery - new driver for Maxim MAX8971 charger - new driver for Huawei Matebook E Go - bq27xxx: retry failed I2C transmissions - bq24190: add BQ24193 support - misc small cleanups and fixes Reset drivers: - new driver for Toradex SMARC Embedded Controller - reboot-mode: add support for modes containing / in DT - atmel,at91sam9260-reset: support sama7d65 - syscon-reboot: add Google GS101 support - misc small cleanups and fixes" * tag 'for-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (28 commits) power: supply: rt9471: Simplify definition of some struct linear_range power: supply: max77976: add EXTCON dependency power: supply: Add support for Maxim MAX8971 charger dt-bindings: power: supply: Document Maxim MAX8971 charger power: supply: max17040: adjust thermal channel scaling power: reset: syscon-reboot: add gs101-specific reset dt-bindings: reset: syscon-reboot: add google,gs101-reboot power: supply: add Huawei Matebook E Go psy driver power: supply: Add driver for Pegatron Chagall battery dt-bindings: power: supply: Document Pegatron Chagall fuel gauge dt-bindings: vendor-prefixes: add prefix for Pegatron Corporation power: supply: cros_charge-control: Avoid -Wflex-array-member-not-at-end warning power: reset: add Toradex Embedded Controller dt-bindings: power: reset: add toradex,smarc-ec power: supply: support charge_types in extensions power: supply: max77705: Fix workqueue error handling in probe power: supply: wm831x: Constify struct chg_map and some arrays power: bq24190: Add BQ24193 support dt-bindings: power: supply: bq24190: Add BQ24193 compatible power: supply: sysfs: Remove duplicate NUL termination ...
This commit is contained in:
commit
c7c1863536
|
|
@ -822,3 +822,46 @@ Description:
|
|||
Each entry is a link to the device which registered the extension.
|
||||
|
||||
Access: Read
|
||||
|
||||
What: /sys/class/power_supply/max8971-charger/fast_charge_timer
|
||||
Date: May 2025
|
||||
KernelVersion: 6.15.0
|
||||
Contact: Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
Description:
|
||||
This entry shows and sets the maximum time the max8971
|
||||
charger operates in fast-charge mode. When the timer expires
|
||||
the device will terminate fast-charge mode (charging current
|
||||
will drop to 0 A) and will trigger interrupt.
|
||||
|
||||
Valid values:
|
||||
|
||||
- 4 - 10 (hours), step by 1
|
||||
- 0: disabled.
|
||||
|
||||
What: /sys/class/power_supply/max8971-charger/top_off_threshold_current
|
||||
Date: May 2025
|
||||
KernelVersion: 6.15.0
|
||||
Contact: Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
Description:
|
||||
This entry shows and sets the charging current threshold for
|
||||
entering top-off charging mode. When charging current in fast
|
||||
charge mode drops below this value, the charger will trigger
|
||||
interrupt and start top-off charging mode.
|
||||
|
||||
Valid values:
|
||||
|
||||
- 50000 - 200000 (microamps), step by 50000 (rounded down)
|
||||
|
||||
What: /sys/class/power_supply/max8971-charger/top_off_timer
|
||||
Date: May 2025
|
||||
KernelVersion: 6.15.0
|
||||
Contact: Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
Description:
|
||||
This entry shows and sets the maximum time the max8971
|
||||
charger operates in top-off charge mode. When the timer expires
|
||||
the device will terminate top-off charge mode (charging current
|
||||
will drop to 0 A) and will trigger interrupt.
|
||||
|
||||
Valid values:
|
||||
|
||||
- 0 - 70 (minutes), step by 10 (rounded down)
|
||||
|
|
|
|||
27
Documentation/ABI/testing/sysfs-class-power-gaokun
Normal file
27
Documentation/ABI/testing/sysfs-class-power-gaokun
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
What: /sys/class/power_supply/gaokun-ec-battery/smart_charge_delay
|
||||
Date: March 2025
|
||||
KernelVersion: 6.15
|
||||
Contact: Pengyu Luo <mitltlatltl@gmail.com>
|
||||
Description:
|
||||
This entry allows configuration of smart charging delay.
|
||||
|
||||
Smart charging behavior: when the power adapter is connected
|
||||
for delay hours, battery charging will follow the rules of
|
||||
charge_control_start_threshold and charge_control_end_threshold.
|
||||
For more information about charge control, please refer to
|
||||
sysfs-class-power.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
Valid values: In hours (non-negative)
|
||||
|
||||
What: /sys/class/power_supply/gaokun-ec-battery/battery_adaptive_charge
|
||||
Date: March 2025
|
||||
KernelVersion: 6.15
|
||||
Contact: Pengyu Luo <mitltlatltl@gmail.com>
|
||||
Description:
|
||||
This entry allows enabling battery adaptive charging.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
Valid values: 0 (disabled) or 1 (enabled)
|
||||
|
|
@ -21,7 +21,9 @@ description: |+
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: syscon-reboot
|
||||
enum:
|
||||
- syscon-reboot
|
||||
- google,gs101-reboot
|
||||
|
||||
mask:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
|
@ -49,12 +51,6 @@ properties:
|
|||
priority:
|
||||
default: 192
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- offset
|
||||
- required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
|
|
@ -63,12 +59,29 @@ additionalProperties: false
|
|||
allOf:
|
||||
- $ref: restart-handler.yaml#
|
||||
- if:
|
||||
not:
|
||||
required:
|
||||
- mask
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: google,gs101-reboot
|
||||
then:
|
||||
required:
|
||||
- value
|
||||
properties:
|
||||
mask: false
|
||||
offset: false
|
||||
reg: false
|
||||
value: false
|
||||
|
||||
else:
|
||||
if:
|
||||
not:
|
||||
required:
|
||||
- mask
|
||||
then:
|
||||
required:
|
||||
- value
|
||||
|
||||
oneOf:
|
||||
- required: [offset]
|
||||
- required: [reg]
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
@ -78,3 +91,8 @@ examples:
|
|||
offset = <0x0>;
|
||||
mask = <0x1>;
|
||||
};
|
||||
|
||||
- |
|
||||
reboot {
|
||||
compatible = "google,gs101-reboot";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/reset/toradex,smarc-ec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Toradex Embedded Controller
|
||||
|
||||
maintainers:
|
||||
- Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
|
||||
- Francesco Dolcini <francesco.dolcini@toradex.com>
|
||||
|
||||
description: |
|
||||
The Toradex Embedded Controller (EC) is used on Toradex SMARC modules,
|
||||
primarily to manage power and reset functionalities.
|
||||
|
||||
The EC provides the following functions:
|
||||
- Reads the SMARC POWER_BTN# and RESET_IN# signals and controls the PMIC accordingly.
|
||||
- Controls the SoC boot mode signals based on the SMARC BOOT_SEL# and FORCE_RECOV# inputs.
|
||||
- Manages the CARRIER_STDBY# signal in response to relevant SoC signals.
|
||||
|
||||
The EC runs a small firmware, factory programmed into its internal flash, and communicates over I2C.
|
||||
It allows software to control power-off and reset functionalities of the module.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- toradex,smarc-imx95-ec
|
||||
- toradex,smarc-imx8mp-ec
|
||||
- const: toradex,smarc-ec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reset-controller@28 {
|
||||
compatible = "toradex,smarc-imx95-ec", "toradex,smarc-ec";
|
||||
reg = <0x28>;
|
||||
};
|
||||
};
|
||||
|
|
@ -19,6 +19,7 @@ properties:
|
|||
- ti,bq24190
|
||||
- ti,bq24192
|
||||
- ti,bq24192i
|
||||
- ti,bq24193
|
||||
- ti,bq24196
|
||||
- ti,bq24296
|
||||
- ti,bq24297
|
||||
|
|
|
|||
|
|
@ -87,28 +87,28 @@ unevaluatedProperties: false
|
|||
examples:
|
||||
- |
|
||||
bat: battery {
|
||||
compatible = "simple-battery";
|
||||
constant-charge-current-max-microamp = <4000000>;
|
||||
constant-charge-voltage-max-microvolt = <8400000>;
|
||||
precharge-current-microamp = <160000>;
|
||||
charge-term-current-microamp = <160000>;
|
||||
compatible = "simple-battery";
|
||||
constant-charge-current-max-microamp = <4000000>;
|
||||
constant-charge-voltage-max-microvolt = <8400000>;
|
||||
precharge-current-microamp = <160000>;
|
||||
charge-term-current-microamp = <160000>;
|
||||
};
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
bq25980: charger@65 {
|
||||
compatible = "ti,bq25980";
|
||||
reg = <0x65>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
|
||||
ti,watchdog-timeout-ms = <0>;
|
||||
ti,sc-ocp-limit-microamp = <2000000>;
|
||||
ti,sc-ovp-limit-microvolt = <17800000>;
|
||||
monitored-battery = <&bat>;
|
||||
};
|
||||
bq25980: charger@65 {
|
||||
compatible = "ti,bq25980";
|
||||
reg = <0x65>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
|
||||
ti,watchdog-timeout-ms = <0>;
|
||||
ti,sc-ocp-limit-microamp = <2000000>;
|
||||
ti,sc-ovp-limit-microvolt = <17800000>;
|
||||
monitored-battery = <&bat>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
|||
|
|
@ -48,14 +48,14 @@ examples:
|
|||
#include <dt-bindings/iio/adc/ingenic,adc.h>
|
||||
|
||||
simple_battery: battery {
|
||||
compatible = "simple-battery";
|
||||
voltage-min-design-microvolt = <3600000>;
|
||||
voltage-max-design-microvolt = <4200000>;
|
||||
compatible = "simple-battery";
|
||||
voltage-min-design-microvolt = <3600000>;
|
||||
voltage-max-design-microvolt = <4200000>;
|
||||
};
|
||||
|
||||
ingenic-battery {
|
||||
compatible = "ingenic,jz4740-battery";
|
||||
io-channels = <&adc INGENIC_ADC_BATTERY>;
|
||||
io-channel-names = "battery";
|
||||
monitored-battery = <&simple_battery>;
|
||||
compatible = "ingenic,jz4740-battery";
|
||||
io-channels = <&adc INGENIC_ADC_BATTERY>;
|
||||
io-channel-names = "battery";
|
||||
monitored-battery = <&simple_battery>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ additionalProperties: false
|
|||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
charger: battery-charger@68 {
|
||||
compatible = "lltc,ltc4162-l";
|
||||
reg = <0x68>;
|
||||
lltc,rsnsb-micro-ohms = <10000>;
|
||||
lltc,rsnsi-micro-ohms = <16000>;
|
||||
lltc,cell-count = <2>;
|
||||
};
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
charger: battery-charger@68 {
|
||||
compatible = "lltc,ltc4162-l";
|
||||
reg = <0x68>;
|
||||
lltc,rsnsb-micro-ohms = <10000>;
|
||||
lltc,rsnsi-micro-ohms = <16000>;
|
||||
lltc,cell-count = <2>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ examples:
|
|||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
charger@69 {
|
||||
compatible = "maxim,max77705-charger";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/maxim,max8971.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX8971 IC charger
|
||||
|
||||
maintainers:
|
||||
- Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
|
||||
description:
|
||||
The MAX8971 is a compact, high-frequency, high-efficiency switch-mode charger
|
||||
for a one-cell lithium-ion (Li+) battery.
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: maxim,max8971
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
monitored-battery: true
|
||||
|
||||
port:
|
||||
description:
|
||||
An optional port node to link the extcon device to detect type of plug.
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
charger@35 {
|
||||
compatible = "maxim,max8971";
|
||||
reg = <0x35>;
|
||||
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <74 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
monitored-battery = <&battery>;
|
||||
|
||||
port {
|
||||
charger_input: endpoint {
|
||||
remote-endpoint = <&extcon_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/pegatron,chagall-ec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Pegatron Chagall EC
|
||||
|
||||
maintainers:
|
||||
- Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
|
||||
description:
|
||||
Pegatron Chagall EC is based on an 8-bit programmable microcontroller from
|
||||
Infineon/Cypress Semiconductor, it communicates over I2C and is used in the
|
||||
Pegatron Chagall tablet for fuel gauge and battery control functions.
|
||||
|
||||
$ref: /schemas/power/supply/power-supply.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: pegatron,chagall-ec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
monitored-battery: true
|
||||
power-supplies: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
embedded-controller@10 {
|
||||
compatible = "pegatron,chagall-ec";
|
||||
reg = <0x10>;
|
||||
|
||||
monitored-battery = <&battery>;
|
||||
power-supplies = <&mains>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -23,6 +23,9 @@ properties:
|
|||
- atmel,sama5d3-rstc
|
||||
- microchip,sam9x60-rstc
|
||||
- microchip,sama7g5-rstc
|
||||
- items:
|
||||
- const: microchip,sama7d65-rstc
|
||||
- const: microchip,sama7g5-rstc
|
||||
- items:
|
||||
- const: atmel,sama5d3-rstc
|
||||
- const: atmel,at91sam9g45-rstc
|
||||
|
|
|
|||
|
|
@ -1158,6 +1158,8 @@ patternProperties:
|
|||
description: Parallax Inc.
|
||||
"^pda,.*":
|
||||
description: Precision Design Associates, Inc.
|
||||
"^pegatron,.*":
|
||||
description: Pegatron Corporation
|
||||
"^pericom,.*":
|
||||
description: Pericom Technology Inc.
|
||||
"^pervasive,.*":
|
||||
|
|
|
|||
|
|
@ -11002,6 +11002,7 @@ M: Pengyu Luo <mitltlatltl@gmail.com>
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml
|
||||
F: drivers/platform/arm64/huawei-gaokun-ec.c
|
||||
F: drivers/power/supply/huawei-gaokun-battery.c
|
||||
F: include/linux/platform_data/huawei-gaokun-ec.h
|
||||
|
||||
HUGETLB SUBSYSTEM
|
||||
|
|
@ -24683,6 +24684,13 @@ L: platform-driver-x86@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/topstar-laptop.c
|
||||
|
||||
TORADEX EMBEDDED CONTROLLER DRIVER
|
||||
M: Emanuele Ghidoli <ghidoliemanuele@gmail.com>
|
||||
M: Francesco Dolcini <francesco@dolcini.it>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/power/reset/toradex,smarc-ec.yaml
|
||||
F: drivers/power/reset/tdx-ec-poweroff.c
|
||||
|
||||
TORTURE-TEST MODULES
|
||||
M: Davidlohr Bueso <dave@stgolabs.net>
|
||||
M: "Paul E. McKenney" <paulmck@kernel.org>
|
||||
|
|
|
|||
|
|
@ -216,6 +216,19 @@ config POWER_RESET_ST
|
|||
help
|
||||
Reset support for STMicroelectronics boards.
|
||||
|
||||
config POWER_RESET_TORADEX_EC
|
||||
tristate "Toradex Embedded Controller power-off and reset driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports power-off and reset for SMARC Toradex SoMs,
|
||||
for example the SMARC iMX8MP and SMARC iMX95, using Toradex
|
||||
Embedded Controller (EC).
|
||||
|
||||
Say Y here if you have a Toradex SMARC SoM.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config POWER_RESET_TPS65086
|
||||
bool "TPS65086 restart driver"
|
||||
depends on MFD_TPS65086
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
|
|||
obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_TORADEX_EC) += tdx-ec-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
|
||||
|
|
|
|||
|
|
@ -129,12 +129,11 @@ static int at91_reset(struct notifier_block *this, unsigned long mode,
|
|||
" str %4, [%0, %6]\n\t"
|
||||
/* Disable SDRAM1 accesses */
|
||||
"1: tst %1, #0\n\t"
|
||||
" beq 2f\n\t"
|
||||
" strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
|
||||
/* Power down SDRAM1 */
|
||||
" strne %4, [%1, %6]\n\t"
|
||||
/* Reset CPU */
|
||||
"2: str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
|
||||
" str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
|
||||
|
||||
" b .\n\t"
|
||||
:
|
||||
|
|
@ -145,7 +144,7 @@ static int at91_reset(struct notifier_block *this, unsigned long mode,
|
|||
"r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN),
|
||||
"r" (reset->data->reset_args),
|
||||
"r" (reset->ramc_lpr)
|
||||
: "r4");
|
||||
);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,20 +23,29 @@ static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
|
|||
const char *cmd)
|
||||
{
|
||||
const char *normal = "normal";
|
||||
int magic = 0;
|
||||
struct mode_info *info;
|
||||
char cmd_[110];
|
||||
|
||||
if (!cmd)
|
||||
cmd = normal;
|
||||
|
||||
list_for_each_entry(info, &reboot->head, list) {
|
||||
if (!strcmp(info->mode, cmd)) {
|
||||
magic = info->magic;
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_for_each_entry(info, &reboot->head, list)
|
||||
if (!strcmp(info->mode, cmd))
|
||||
return info->magic;
|
||||
|
||||
return magic;
|
||||
/* try to match again, replacing characters impossible in DT */
|
||||
if (strscpy(cmd_, cmd, sizeof(cmd_)) == -E2BIG)
|
||||
return 0;
|
||||
|
||||
strreplace(cmd_, ' ', '-');
|
||||
strreplace(cmd_, ',', '-');
|
||||
strreplace(cmd_, '/', '-');
|
||||
|
||||
list_for_each_entry(info, &reboot->head, list)
|
||||
if (!strcmp(info->mode, cmd_))
|
||||
return info->magic;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reboot_mode_notify(struct notifier_block *this,
|
||||
|
|
|
|||
|
|
@ -14,11 +14,24 @@
|
|||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct reboot_mode_bits {
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
u32 value;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct reboot_data {
|
||||
struct reboot_mode_bits mode_bits[REBOOT_SOFT + 1];
|
||||
struct reboot_mode_bits catchall;
|
||||
};
|
||||
|
||||
struct syscon_reboot_context {
|
||||
struct regmap *map;
|
||||
u32 offset;
|
||||
u32 value;
|
||||
u32 mask;
|
||||
|
||||
const struct reboot_data *rd; /* from of match data, if any */
|
||||
struct reboot_mode_bits catchall; /* from DT */
|
||||
|
||||
struct notifier_block restart_handler;
|
||||
};
|
||||
|
||||
|
|
@ -28,9 +41,21 @@ static int syscon_restart_handle(struct notifier_block *this,
|
|||
struct syscon_reboot_context *ctx =
|
||||
container_of(this, struct syscon_reboot_context,
|
||||
restart_handler);
|
||||
const struct reboot_mode_bits *mode_bits;
|
||||
|
||||
if (ctx->rd) {
|
||||
if (mode < ARRAY_SIZE(ctx->rd->mode_bits) &&
|
||||
ctx->rd->mode_bits[mode].valid)
|
||||
mode_bits = &ctx->rd->mode_bits[mode];
|
||||
else
|
||||
mode_bits = &ctx->rd->catchall;
|
||||
} else {
|
||||
mode_bits = &ctx->catchall;
|
||||
}
|
||||
|
||||
/* Issue the reboot */
|
||||
regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value);
|
||||
regmap_update_bits(ctx->map, mode_bits->offset, mode_bits->mask,
|
||||
mode_bits->value);
|
||||
|
||||
mdelay(1000);
|
||||
|
||||
|
|
@ -42,7 +67,6 @@ static int syscon_reboot_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct syscon_reboot_context *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
int mask_err, value_err;
|
||||
int priority;
|
||||
int err;
|
||||
|
||||
|
|
@ -60,24 +84,33 @@ static int syscon_reboot_probe(struct platform_device *pdev)
|
|||
if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
|
||||
priority = 192;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
|
||||
if (of_property_read_u32(pdev->dev.of_node, "reg", &ctx->offset))
|
||||
ctx->rd = of_device_get_match_data(dev);
|
||||
if (!ctx->rd) {
|
||||
int mask_err, value_err;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "offset",
|
||||
&ctx->catchall.offset) &&
|
||||
of_property_read_u32(pdev->dev.of_node, "reg",
|
||||
&ctx->catchall.offset))
|
||||
return -EINVAL;
|
||||
|
||||
value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value);
|
||||
mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask);
|
||||
if (value_err && mask_err) {
|
||||
dev_err(dev, "unable to read 'value' and 'mask'");
|
||||
return -EINVAL;
|
||||
}
|
||||
value_err = of_property_read_u32(pdev->dev.of_node, "value",
|
||||
&ctx->catchall.value);
|
||||
mask_err = of_property_read_u32(pdev->dev.of_node, "mask",
|
||||
&ctx->catchall.mask);
|
||||
if (value_err && mask_err) {
|
||||
dev_err(dev, "unable to read 'value' and 'mask'");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value_err) {
|
||||
/* support old binding */
|
||||
ctx->value = ctx->mask;
|
||||
ctx->mask = 0xFFFFFFFF;
|
||||
} else if (mask_err) {
|
||||
/* support value without mask*/
|
||||
ctx->mask = 0xFFFFFFFF;
|
||||
if (value_err) {
|
||||
/* support old binding */
|
||||
ctx->catchall.value = ctx->catchall.mask;
|
||||
ctx->catchall.mask = 0xFFFFFFFF;
|
||||
} else if (mask_err) {
|
||||
/* support value without mask */
|
||||
ctx->catchall.mask = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->restart_handler.notifier_call = syscon_restart_handle;
|
||||
|
|
@ -89,7 +122,30 @@ static int syscon_reboot_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static const struct reboot_data gs101_reboot_data = {
|
||||
.mode_bits = {
|
||||
[REBOOT_WARM] = {
|
||||
.offset = 0x3a00, /* SYSTEM_CONFIGURATION */
|
||||
.mask = 0x00000002, /* SWRESET_SYSTEM */
|
||||
.value = 0x00000002,
|
||||
.valid = true,
|
||||
},
|
||||
[REBOOT_SOFT] = {
|
||||
.offset = 0x3a00, /* SYSTEM_CONFIGURATION */
|
||||
.mask = 0x00000002, /* SWRESET_SYSTEM */
|
||||
.value = 0x00000002,
|
||||
.valid = true,
|
||||
},
|
||||
},
|
||||
.catchall = {
|
||||
.offset = 0x3e9c, /* PAD_CTRL_PWR_HOLD */
|
||||
.mask = 0x00000100,
|
||||
.value = 0x00000000,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id syscon_reboot_of_match[] = {
|
||||
{ .compatible = "google,gs101-reboot", .data = &gs101_reboot_data },
|
||||
{ .compatible = "syscon-reboot" },
|
||||
{}
|
||||
};
|
||||
|
|
|
|||
150
drivers/power/reset/tdx-ec-poweroff.c
Normal file
150
drivers/power/reset/tdx-ec-poweroff.c
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Toradex Embedded Controller driver
|
||||
*
|
||||
* Copyright (C) 2025 Toradex
|
||||
*
|
||||
* Author: Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define EC_CHIP_ID_REG 0x00
|
||||
#define EC_CHIP_ID_SMARC_IMX95 0x11
|
||||
#define EC_CHIP_ID_SMARC_IMX8MP 0x12
|
||||
|
||||
#define EC_VERSION_REG_MAJOR 0x01
|
||||
#define EC_VERSION_REG_MINOR 0x02
|
||||
#define EC_ID_VERSION_LEN 3
|
||||
|
||||
#define EC_CMD_REG 0xD0
|
||||
#define EC_CMD_POWEROFF 0x01
|
||||
#define EC_CMD_RESET 0x02
|
||||
|
||||
#define EC_REG_MAX 0xD0
|
||||
|
||||
static const struct regmap_range volatile_ranges[] = {
|
||||
regmap_reg_range(EC_CMD_REG, EC_CMD_REG),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table volatile_table = {
|
||||
.yes_ranges = volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range read_ranges[] = {
|
||||
regmap_reg_range(EC_CHIP_ID_REG, EC_VERSION_REG_MINOR),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table read_table = {
|
||||
.yes_ranges = read_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(read_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = EC_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.rd_table = &read_table,
|
||||
.volatile_table = &volatile_table,
|
||||
};
|
||||
|
||||
static int tdx_ec_cmd(struct regmap *regmap, u8 cmd)
|
||||
{
|
||||
int err = regmap_write(regmap, EC_CMD_REG, cmd);
|
||||
|
||||
if (err)
|
||||
dev_err(regmap_get_device(regmap), "Failed to send command 0x%02X: %d\n", cmd, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tdx_ec_power_off(struct sys_off_data *data)
|
||||
{
|
||||
struct regmap *regmap = data->cb_data;
|
||||
int err;
|
||||
|
||||
err = tdx_ec_cmd(regmap, EC_CMD_POWEROFF);
|
||||
|
||||
return err ? NOTIFY_BAD : NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int tdx_ec_restart(struct sys_off_data *data)
|
||||
{
|
||||
struct regmap *regmap = data->cb_data;
|
||||
int err;
|
||||
|
||||
err = tdx_ec_cmd(regmap, EC_CMD_RESET);
|
||||
|
||||
return err ? NOTIFY_BAD : NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int tdx_ec_register_power_off_restart(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART,
|
||||
SYS_OFF_PRIO_FIRMWARE,
|
||||
tdx_ec_restart, regmap);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,
|
||||
SYS_OFF_PRIO_FIRMWARE,
|
||||
tdx_ec_power_off, regmap);
|
||||
}
|
||||
|
||||
static int tdx_ec_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
u8 reg_val[EC_ID_VERSION_LEN];
|
||||
struct regmap *regmap;
|
||||
int err;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
err = regmap_bulk_read(regmap, EC_CHIP_ID_REG, ®_val, EC_ID_VERSION_LEN);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"Cannot read id and version registers\n");
|
||||
|
||||
dev_info(dev, "Toradex Embedded Controller id %x - Firmware %u.%u\n",
|
||||
reg_val[0], reg_val[1], reg_val[2]);
|
||||
|
||||
err = tdx_ec_register_power_off_restart(dev, regmap);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"Cannot register system restart handler\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused of_tdx_ec_match[] = {
|
||||
{ .compatible = "toradex,smarc-ec" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_tdx_ec_match);
|
||||
|
||||
static struct i2c_driver tdx_ec_driver = {
|
||||
.probe = tdx_ec_probe,
|
||||
.driver = {
|
||||
.name = "toradex-smarc-ec",
|
||||
.of_match_table = of_tdx_ec_match,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(tdx_ec_driver);
|
||||
|
||||
MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>");
|
||||
MODULE_DESCRIPTION("Toradex SMARC Embedded Controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -107,6 +107,18 @@ config BATTERY_ACT8945A
|
|||
Say Y here to enable support for power supply provided by
|
||||
Active-semi ActivePath ACT8945A charger.
|
||||
|
||||
config BATTERY_CHAGALL
|
||||
tristate "Pegatron Chagall battery driver"
|
||||
depends on I2C
|
||||
depends on LEDS_CLASS
|
||||
help
|
||||
Say Y to include support for Cypress CG7153AM IC based battery
|
||||
fuel gauge with custom firmware found in Pegatron Chagall based
|
||||
tablet line.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called chagall-battery.
|
||||
|
||||
config BATTERY_CPCAP
|
||||
tristate "Motorola CPCAP PMIC battery driver"
|
||||
depends on MFD_CPCAP && IIO
|
||||
|
|
@ -161,6 +173,16 @@ config BATTERY_DS2782
|
|||
Say Y here to enable support for the DS2782/DS2786 standalone battery
|
||||
gas-gauge.
|
||||
|
||||
config BATTERY_HUAWEI_GAOKUN
|
||||
tristate "Huawei Matebook E Go power supply"
|
||||
depends on EC_HUAWEI_GAOKUN
|
||||
help
|
||||
This driver enables battery and adapter support on the Huawei Matebook
|
||||
E Go, which is a sc8280xp-based 2-in-1 tablet.
|
||||
|
||||
To compile the driver as a module, choose M here: the module will be
|
||||
called huawei-gaokun-battery.
|
||||
|
||||
config BATTERY_LEGO_EV3
|
||||
tristate "LEGO MINDSTORMS EV3 battery"
|
||||
depends on OF && IIO && GPIOLIB && (ARCH_DAVINCI_DA850 || COMPILE_TEST)
|
||||
|
|
@ -595,6 +617,21 @@ config CHARGER_MAX77976
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called max77976_charger.
|
||||
|
||||
config CHARGER_MAX8971
|
||||
tristate "Maxim MAX8971 battery charger driver"
|
||||
depends on I2C
|
||||
depends on EXTCON || !EXTCON
|
||||
select REGMAP_I2C
|
||||
help
|
||||
The MAX8971 is a compact, high-frequency, high-efficiency switch-mode
|
||||
charger for a one-cell lithium-ion (Li+) battery. It delivers up to
|
||||
1.55A of current to the battery from inputs up to 7.5V and withstands
|
||||
transient inputs up to 22V.
|
||||
|
||||
Say Y to enable support for the Maxim MAX8971 battery charger.
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called max8971_charger.
|
||||
|
||||
config CHARGER_MAX8997
|
||||
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
|
||||
depends on MFD_MAX8997 && REGULATOR_MAX8997
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_CHARGER_ADP5061) += adp5061.o
|
|||
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_CHAGALL) += chagall-battery.o
|
||||
obj-$(CONFIG_BATTERY_CPCAP) += cpcap-battery.o
|
||||
obj-$(CONFIG_BATTERY_CW2015) += cw2015_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
|
|
@ -31,6 +32,7 @@ obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
|
|||
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
||||
obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o
|
||||
obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
|
||||
obj-$(CONFIG_BATTERY_HUAWEI_GAOKUN) += huawei-gaokun-battery.o
|
||||
obj-$(CONFIG_BATTERY_LEGO_EV3) += lego_ev3_battery.o
|
||||
obj-$(CONFIG_BATTERY_LENOVO_YOGA_C630) += lenovo_yoga_c630_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
||||
|
|
@ -81,6 +83,7 @@ obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o
|
|||
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8971) += max8971_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ enum bq24190_chip {
|
|||
BQ24190,
|
||||
BQ24192,
|
||||
BQ24192i,
|
||||
BQ24193,
|
||||
BQ24196,
|
||||
BQ24296,
|
||||
BQ24297,
|
||||
|
|
@ -2014,6 +2015,17 @@ static const struct bq24190_chip_info bq24190_chip_info_tbl[] = {
|
|||
.ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values),
|
||||
#ifdef CONFIG_REGULATOR
|
||||
.vbus_desc = &bq24190_vbus_desc,
|
||||
#endif
|
||||
.check_chip = bq24190_check_chip,
|
||||
.set_chg_config = bq24190_battery_set_chg_config,
|
||||
.ntc_fault_mask = BQ24190_REG_F_NTC_FAULT_MASK,
|
||||
.get_ntc_status = bq24190_charger_get_ntc_status,
|
||||
.set_otg_vbus = bq24190_set_otg_vbus,
|
||||
},
|
||||
[BQ24193] = {
|
||||
.ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values),
|
||||
#ifdef CONFIG_REGULATOR
|
||||
.vbus_desc = &bq24190_vbus_desc,
|
||||
#endif
|
||||
.check_chip = bq24190_check_chip,
|
||||
.set_chg_config = bq24190_battery_set_chg_config,
|
||||
|
|
@ -2308,6 +2320,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
|
|||
{ "bq24190", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] },
|
||||
{ "bq24192", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] },
|
||||
{ "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] },
|
||||
{ "bq24193", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24193] },
|
||||
{ "bq24196", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] },
|
||||
{ "bq24296", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] },
|
||||
{ "bq24297", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] },
|
||||
|
|
@ -2319,6 +2332,7 @@ static const struct of_device_id bq24190_of_match[] = {
|
|||
{ .compatible = "ti,bq24190", .data = &bq24190_chip_info_tbl[BQ24190] },
|
||||
{ .compatible = "ti,bq24192", .data = &bq24190_chip_info_tbl[BQ24192] },
|
||||
{ .compatible = "ti,bq24192i", .data = &bq24190_chip_info_tbl[BQ24192i] },
|
||||
{ .compatible = "ti,bq24193", .data = &bq24190_chip_info_tbl[BQ24193] },
|
||||
{ .compatible = "ti,bq24196", .data = &bq24190_chip_info_tbl[BQ24196] },
|
||||
{ .compatible = "ti,bq24296", .data = &bq24190_chip_info_tbl[BQ24296] },
|
||||
{ .compatible = "ti,bq24297", .data = &bq24190_chip_info_tbl[BQ24297] },
|
||||
|
|
|
|||
|
|
@ -2131,7 +2131,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
|
|||
mutex_unlock(&di->lock);
|
||||
|
||||
if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
|
||||
return -ENODEV;
|
||||
return di->cache.flags;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* Andrew F. Davis <afd@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -31,6 +32,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
|
|||
struct i2c_msg msg[2];
|
||||
u8 data[2];
|
||||
int ret;
|
||||
int retry = 0;
|
||||
|
||||
if (!client->adapter)
|
||||
return -ENODEV;
|
||||
|
|
@ -47,7 +49,16 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
|
|||
else
|
||||
msg[1].len = 2;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
|
||||
do {
|
||||
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
|
||||
if (ret == -EBUSY && ++retry < 3) {
|
||||
/* sleep 10 milliseconds when busy */
|
||||
usleep_range(10000, 11000);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
291
drivers/power/supply/chagall-battery.c
Normal file
291
drivers/power/supply/chagall-battery.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define CHAGALL_REG_LED_AMBER 0x60
|
||||
#define CHAGALL_REG_LED_WHITE 0x70
|
||||
#define CHAGALL_REG_BATTERY_TEMPERATURE 0xa2
|
||||
#define CHAGALL_REG_BATTERY_VOLTAGE 0xa4
|
||||
#define CHAGALL_REG_BATTERY_CURRENT 0xa6
|
||||
#define CHAGALL_REG_BATTERY_CAPACITY 0xa8
|
||||
#define CHAGALL_REG_BATTERY_CHARGING_CURRENT 0xaa
|
||||
#define CHAGALL_REG_BATTERY_CHARGING_VOLTAGE 0xac
|
||||
#define CHAGALL_REG_BATTERY_STATUS 0xae
|
||||
#define BATTERY_DISCHARGING BIT(6)
|
||||
#define BATTERY_FULL_CHARGED BIT(5)
|
||||
#define BATTERY_FULL_DISCHARGED BIT(4)
|
||||
#define CHAGALL_REG_BATTERY_REMAIN_CAPACITY 0xb0
|
||||
#define CHAGALL_REG_BATTERY_FULL_CAPACITY 0xb2
|
||||
#define CHAGALL_REG_MAX_COUNT 0xb4
|
||||
|
||||
#define CHAGALL_BATTERY_DATA_REFRESH 5000
|
||||
#define TEMP_CELSIUS_OFFSET 2731
|
||||
|
||||
static const struct regmap_config chagall_battery_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = CHAGALL_REG_MAX_COUNT,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
struct chagall_battery_data {
|
||||
struct regmap *regmap;
|
||||
struct led_classdev amber_led;
|
||||
struct led_classdev white_led;
|
||||
struct power_supply *battery;
|
||||
struct delayed_work poll_work;
|
||||
u16 last_state;
|
||||
};
|
||||
|
||||
static void chagall_led_set_brightness_amber(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct chagall_battery_data *cg =
|
||||
container_of(led, struct chagall_battery_data, amber_led);
|
||||
|
||||
regmap_write(cg->regmap, CHAGALL_REG_LED_AMBER, brightness);
|
||||
}
|
||||
|
||||
static void chagall_led_set_brightness_white(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct chagall_battery_data *cg =
|
||||
container_of(led, struct chagall_battery_data, white_led);
|
||||
|
||||
regmap_write(cg->regmap, CHAGALL_REG_LED_WHITE, brightness);
|
||||
}
|
||||
|
||||
static const enum power_supply_property chagall_battery_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
};
|
||||
|
||||
static const unsigned int chagall_battery_prop_offs[] = {
|
||||
[POWER_SUPPLY_PROP_STATUS] = CHAGALL_REG_BATTERY_STATUS,
|
||||
[POWER_SUPPLY_PROP_VOLTAGE_NOW] = CHAGALL_REG_BATTERY_VOLTAGE,
|
||||
[POWER_SUPPLY_PROP_VOLTAGE_MAX] = CHAGALL_REG_BATTERY_CHARGING_VOLTAGE,
|
||||
[POWER_SUPPLY_PROP_CURRENT_NOW] = CHAGALL_REG_BATTERY_CURRENT,
|
||||
[POWER_SUPPLY_PROP_CURRENT_MAX] = CHAGALL_REG_BATTERY_CHARGING_CURRENT,
|
||||
[POWER_SUPPLY_PROP_CAPACITY] = CHAGALL_REG_BATTERY_CAPACITY,
|
||||
[POWER_SUPPLY_PROP_TEMP] = CHAGALL_REG_BATTERY_TEMPERATURE,
|
||||
[POWER_SUPPLY_PROP_CHARGE_FULL] = CHAGALL_REG_BATTERY_FULL_CAPACITY,
|
||||
[POWER_SUPPLY_PROP_CHARGE_NOW] = CHAGALL_REG_BATTERY_REMAIN_CAPACITY,
|
||||
};
|
||||
|
||||
static int chagall_battery_get_value(struct chagall_battery_data *cg,
|
||||
enum power_supply_property psp, u32 *val)
|
||||
{
|
||||
if (psp >= ARRAY_SIZE(chagall_battery_prop_offs))
|
||||
return -EINVAL;
|
||||
if (!chagall_battery_prop_offs[psp])
|
||||
return -EINVAL;
|
||||
|
||||
/* Battery data is stored in 2 consecutive registers with little-endian */
|
||||
return regmap_bulk_read(cg->regmap, chagall_battery_prop_offs[psp], val, 2);
|
||||
}
|
||||
|
||||
static int chagall_battery_get_status(u32 status_reg)
|
||||
{
|
||||
if (status_reg & BATTERY_FULL_CHARGED)
|
||||
return POWER_SUPPLY_STATUS_FULL;
|
||||
else if (status_reg & BATTERY_DISCHARGING)
|
||||
return POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else
|
||||
return POWER_SUPPLY_STATUS_CHARGING;
|
||||
}
|
||||
|
||||
static int chagall_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct chagall_battery_data *cg = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = chagall_battery_get_value(cg, psp, &val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
val->intval -= TEMP_CELSIUS_OFFSET;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
val->intval *= 1000;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = chagall_battery_get_status(val->intval);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chagall_battery_poll_work(struct work_struct *work)
|
||||
{
|
||||
struct chagall_battery_data *cg =
|
||||
container_of(work, struct chagall_battery_data, poll_work.work);
|
||||
u32 state;
|
||||
int ret;
|
||||
|
||||
ret = chagall_battery_get_value(cg, POWER_SUPPLY_PROP_STATUS, &state);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
state = chagall_battery_get_status(state);
|
||||
|
||||
if (cg->last_state != state) {
|
||||
cg->last_state = state;
|
||||
power_supply_changed(cg->battery);
|
||||
}
|
||||
|
||||
/* continuously send uevent notification */
|
||||
schedule_delayed_work(&cg->poll_work,
|
||||
msecs_to_jiffies(CHAGALL_BATTERY_DATA_REFRESH));
|
||||
}
|
||||
|
||||
static const struct power_supply_desc chagall_battery_desc = {
|
||||
.name = "chagall-battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = chagall_battery_properties,
|
||||
.num_properties = ARRAY_SIZE(chagall_battery_properties),
|
||||
.get_property = chagall_battery_get_property,
|
||||
.external_power_changed = power_supply_changed,
|
||||
};
|
||||
|
||||
static int chagall_battery_probe(struct i2c_client *client)
|
||||
{
|
||||
struct chagall_battery_data *cg;
|
||||
struct device *dev = &client->dev;
|
||||
struct power_supply_config cfg = { };
|
||||
int ret;
|
||||
|
||||
cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL);
|
||||
if (!cg)
|
||||
return -ENOMEM;
|
||||
|
||||
cfg.drv_data = cg;
|
||||
cfg.fwnode = dev_fwnode(dev);
|
||||
|
||||
i2c_set_clientdata(client, cg);
|
||||
|
||||
cg->regmap = devm_regmap_init_i2c(client, &chagall_battery_regmap_config);
|
||||
if (IS_ERR(cg->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(cg->regmap), "cannot allocate regmap\n");
|
||||
|
||||
cg->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
cg->battery = devm_power_supply_register(dev, &chagall_battery_desc, &cfg);
|
||||
if (IS_ERR(cg->battery))
|
||||
return dev_err_probe(dev, PTR_ERR(cg->battery),
|
||||
"failed to register power supply\n");
|
||||
|
||||
cg->amber_led.name = "power::amber";
|
||||
cg->amber_led.max_brightness = 1;
|
||||
cg->amber_led.flags = LED_CORE_SUSPENDRESUME;
|
||||
cg->amber_led.brightness_set = chagall_led_set_brightness_amber;
|
||||
cg->amber_led.default_trigger = "chagall-battery-charging";
|
||||
|
||||
ret = devm_led_classdev_register(dev, &cg->amber_led);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register amber LED\n");
|
||||
|
||||
cg->white_led.name = "power::white";
|
||||
cg->white_led.max_brightness = 1;
|
||||
cg->white_led.flags = LED_CORE_SUSPENDRESUME;
|
||||
cg->white_led.brightness_set = chagall_led_set_brightness_white;
|
||||
cg->white_led.default_trigger = "chagall-battery-full";
|
||||
|
||||
ret = devm_led_classdev_register(dev, &cg->white_led);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register white LED\n");
|
||||
|
||||
led_set_brightness(&cg->amber_led, LED_OFF);
|
||||
led_set_brightness(&cg->white_led, LED_OFF);
|
||||
|
||||
ret = devm_delayed_work_autocancel(dev, &cg->poll_work, chagall_battery_poll_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
schedule_delayed_work(&cg->poll_work, msecs_to_jiffies(CHAGALL_BATTERY_DATA_REFRESH));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused chagall_battery_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct chagall_battery_data *cg = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&cg->poll_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused chagall_battery_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct chagall_battery_data *cg = i2c_get_clientdata(client);
|
||||
|
||||
schedule_delayed_work(&cg->poll_work, msecs_to_jiffies(CHAGALL_BATTERY_DATA_REFRESH));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(chagall_battery_pm_ops,
|
||||
chagall_battery_suspend, chagall_battery_resume);
|
||||
|
||||
static const struct of_device_id chagall_of_match[] = {
|
||||
{ .compatible = "pegatron,chagall-ec" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, chagall_of_match);
|
||||
|
||||
static struct i2c_driver chagall_battery_driver = {
|
||||
.driver = {
|
||||
.name = "chagall-battery",
|
||||
.pm = &chagall_battery_pm_ops,
|
||||
.of_match_table = chagall_of_match,
|
||||
},
|
||||
.probe = chagall_battery_probe,
|
||||
};
|
||||
module_i2c_driver(chagall_battery_driver);
|
||||
|
||||
MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
|
||||
MODULE_DESCRIPTION("Pegatron Chagall fuel gauge driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -440,6 +440,7 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
|
|||
|
||||
static void collie_bat_remove(struct ucb1x00_dev *dev)
|
||||
{
|
||||
device_init_wakeup(&ucb->dev, 0);
|
||||
free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main);
|
||||
power_supply_unregister(collie_bat_bu.psy);
|
||||
power_supply_unregister(collie_bat_main.psy);
|
||||
|
|
|
|||
|
|
@ -47,29 +47,20 @@ struct cros_chctl_priv {
|
|||
static int cros_chctl_send_charge_control_cmd(struct cros_ec_device *cros_ec,
|
||||
u8 cmd_version, struct ec_params_charge_control *req)
|
||||
{
|
||||
int ret;
|
||||
static const u8 outsizes[] = {
|
||||
[1] = offsetof(struct ec_params_charge_control, cmd),
|
||||
[2] = sizeof(struct ec_params_charge_control),
|
||||
[3] = sizeof(struct ec_params_charge_control),
|
||||
};
|
||||
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
union {
|
||||
struct ec_params_charge_control req;
|
||||
struct ec_response_charge_control resp;
|
||||
} __packed data;
|
||||
} __packed buf = {
|
||||
.msg = {
|
||||
.command = EC_CMD_CHARGE_CONTROL,
|
||||
.version = cmd_version,
|
||||
.insize = 0,
|
||||
.outsize = outsizes[cmd_version],
|
||||
},
|
||||
.data.req = *req,
|
||||
};
|
||||
ret = cros_ec_cmd(cros_ec, cmd_version, EC_CMD_CHARGE_CONTROL, req,
|
||||
outsizes[cmd_version], NULL, 0);
|
||||
|
||||
return cros_ec_cmd_xfer_status(cros_ec, &buf.msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
|
||||
|
|
|
|||
|
|
@ -366,7 +366,9 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, gpio_charger);
|
||||
|
||||
device_init_wakeup(dev, 1);
|
||||
ret = devm_device_init_wakeup(dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init wakeup\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
645
drivers/power/supply/huawei-gaokun-battery.c
Normal file
645
drivers/power/supply/huawei-gaokun-battery.c
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* huawei-gaokun-battery - A power supply driver for HUAWEI Matebook E Go
|
||||
*
|
||||
* Copyright (C) 2024 Pengyu Luo <mitltlatltl@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_data/huawei-gaokun-ec.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/sprintf.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* String Data Reg */
|
||||
|
||||
#define EC_BAT_VENDOR 0x01 /* from 0x01 to 0x0F, SUNWODA */
|
||||
#define EC_BAT_MODEL 0x11 /* from 0x11 to 0x1F, HB30A8P9ECW-22T */
|
||||
|
||||
#define EC_ADP_STATUS 0x81
|
||||
#define EC_AC_STATUS BIT(0)
|
||||
#define EC_BAT_PRESENT BIT(1) /* BATC._STA */
|
||||
|
||||
#define EC_BAT_STATUS 0x82 /* _BST */
|
||||
#define EC_BAT_DISCHARGING BIT(0)
|
||||
#define EC_BAT_CHARGING BIT(1)
|
||||
#define EC_BAT_CRITICAL BIT(2) /* Low Battery Level */
|
||||
#define EC_BAT_FULL BIT(3)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Word Data Reg */
|
||||
|
||||
/* 0x5A: ?
|
||||
* 0x5C: ?
|
||||
* 0x5E: ?
|
||||
* 0X60: ?
|
||||
* 0x84: ?
|
||||
*/
|
||||
|
||||
#define EC_BAT_STATUS_START 0x90
|
||||
#define EC_BAT_PERCENTAGE 0x90
|
||||
#define EC_BAT_VOLTAGE 0x92
|
||||
#define EC_BAT_CAPACITY 0x94
|
||||
#define EC_BAT_FULL_CAPACITY 0x96
|
||||
/* 0x98: ? */
|
||||
#define EC_BAT_CURRENT 0x9A
|
||||
/* 0x9C: ? */
|
||||
|
||||
#define EC_BAT_INFO_START 0xA0
|
||||
/* 0xA0: POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT? */
|
||||
#define EC_BAT_DESIGN_CAPACITY 0xA2
|
||||
#define EC_BAT_DESIGN_VOLTAGE 0xA4
|
||||
#define EC_BAT_SERIAL_NUMBER 0xA6
|
||||
#define EC_BAT_CYCLE_COUNT 0xAA
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Battery Event ID */
|
||||
|
||||
#define EC_EVENT_BAT_A0 0xA0
|
||||
#define EC_EVENT_BAT_A1 0xA1
|
||||
#define EC_EVENT_BAT_A2 0xA2
|
||||
#define EC_EVENT_BAT_A3 0xA3
|
||||
#define EC_EVENT_BAT_B1 0xB1
|
||||
/* EVENT B1 A0 A1 repeat about every 1s 2s 3s respectively */
|
||||
|
||||
/* ACPI _BIX field, Min sampling time, the duration between two _BST */
|
||||
#define CACHE_TIME 2000 /* cache time in milliseconds */
|
||||
|
||||
#define MILLI_TO_MICRO 1000
|
||||
|
||||
#define SMART_CHARGE_MODE 0
|
||||
#define SMART_CHARGE_DELAY 1
|
||||
#define SMART_CHARGE_START 2
|
||||
#define SMART_CHARGE_END 3
|
||||
|
||||
#define NO_DELAY_MODE 1
|
||||
#define DELAY_MODE 4
|
||||
|
||||
struct gaokun_psy_bat_status {
|
||||
__le16 percentage_now; /* 0x90 */
|
||||
__le16 voltage_now;
|
||||
__le16 capacity_now;
|
||||
__le16 full_capacity;
|
||||
__le16 unknown1;
|
||||
__le16 rate_now;
|
||||
__le16 unknown2; /* 0x9C */
|
||||
} __packed;
|
||||
|
||||
struct gaokun_psy_bat_info {
|
||||
__le16 unknown3; /* 0xA0 */
|
||||
__le16 design_capacity;
|
||||
__le16 design_voltage;
|
||||
__le16 serial_number;
|
||||
__le16 padding2;
|
||||
__le16 cycle_count; /* 0xAA */
|
||||
} __packed;
|
||||
|
||||
struct gaokun_psy {
|
||||
struct gaokun_ec *ec;
|
||||
struct device *dev;
|
||||
struct notifier_block nb;
|
||||
|
||||
struct power_supply *bat_psy;
|
||||
struct power_supply *adp_psy;
|
||||
|
||||
unsigned long update_time;
|
||||
struct gaokun_psy_bat_status status;
|
||||
struct gaokun_psy_bat_info info;
|
||||
|
||||
char battery_model[0x10]; /* HB30A8P9ECW-22T, the real one is XXX-22A */
|
||||
char battery_serial[0x10];
|
||||
char battery_vendor[0x10];
|
||||
|
||||
int charge_now;
|
||||
int online;
|
||||
int bat_present;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Adapter */
|
||||
|
||||
static int gaokun_psy_get_adp_status(struct gaokun_psy *ecbat)
|
||||
{
|
||||
/* _PSR */
|
||||
int ret;
|
||||
u8 online;
|
||||
|
||||
ret = gaokun_ec_psy_read_byte(ecbat->ec, EC_ADP_STATUS, &online);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ecbat->online = !!(online & EC_AC_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gaokun_psy_get_adp_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
ret = gaokun_psy_get_adp_status(ecbat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = ecbat->online;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
val->intval = POWER_SUPPLY_USB_TYPE_C;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property gaokun_psy_adp_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc gaokun_psy_adp_desc = {
|
||||
.name = "gaokun-ec-adapter",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C),
|
||||
.get_property = gaokun_psy_get_adp_property,
|
||||
.properties = gaokun_psy_adp_props,
|
||||
.num_properties = ARRAY_SIZE(gaokun_psy_adp_props),
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Battery */
|
||||
|
||||
static inline void gaokun_psy_get_bat_present(struct gaokun_psy *ecbat)
|
||||
{
|
||||
int ret;
|
||||
u8 present;
|
||||
|
||||
/* Some kind of initialization */
|
||||
gaokun_ec_write(ecbat->ec, (u8 []){0x02, 0xB2, 1, 0x90});
|
||||
|
||||
ret = gaokun_ec_psy_read_byte(ecbat->ec, EC_ADP_STATUS, &present);
|
||||
|
||||
ecbat->bat_present = ret ? false : !!(present & EC_BAT_PRESENT);
|
||||
}
|
||||
|
||||
static inline int gaokun_psy_bat_present(struct gaokun_psy *ecbat)
|
||||
{
|
||||
return ecbat->bat_present;
|
||||
}
|
||||
|
||||
static int gaokun_psy_get_bat_info(struct gaokun_psy *ecbat)
|
||||
{
|
||||
/* _BIX */
|
||||
if (!gaokun_psy_bat_present(ecbat))
|
||||
return 0;
|
||||
|
||||
return gaokun_ec_psy_multi_read(ecbat->ec, EC_BAT_INFO_START,
|
||||
sizeof(ecbat->info), (u8 *)&ecbat->info);
|
||||
}
|
||||
|
||||
static void gaokun_psy_update_bat_charge(struct gaokun_psy *ecbat)
|
||||
{
|
||||
u8 charge;
|
||||
|
||||
gaokun_ec_psy_read_byte(ecbat->ec, EC_BAT_STATUS, &charge);
|
||||
|
||||
switch (charge) {
|
||||
case EC_BAT_CHARGING:
|
||||
ecbat->charge_now = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case EC_BAT_DISCHARGING:
|
||||
ecbat->charge_now = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case EC_BAT_FULL:
|
||||
ecbat->charge_now = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(ecbat->dev, "unknown charge state %d\n", charge);
|
||||
}
|
||||
}
|
||||
|
||||
static int gaokun_psy_get_bat_status(struct gaokun_psy *ecbat)
|
||||
{
|
||||
/* _BST */
|
||||
int ret;
|
||||
|
||||
if (time_before(jiffies, ecbat->update_time +
|
||||
msecs_to_jiffies(CACHE_TIME)))
|
||||
return 0;
|
||||
|
||||
gaokun_psy_update_bat_charge(ecbat);
|
||||
ret = gaokun_ec_psy_multi_read(ecbat->ec, EC_BAT_STATUS_START,
|
||||
sizeof(ecbat->status), (u8 *)&ecbat->status);
|
||||
|
||||
ecbat->update_time = jiffies;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gaokun_psy_init(struct gaokun_psy *ecbat)
|
||||
{
|
||||
gaokun_psy_get_bat_present(ecbat);
|
||||
if (!gaokun_psy_bat_present(ecbat))
|
||||
return;
|
||||
|
||||
gaokun_psy_get_bat_info(ecbat);
|
||||
|
||||
snprintf(ecbat->battery_serial, sizeof(ecbat->battery_serial),
|
||||
"%d", le16_to_cpu(ecbat->info.serial_number));
|
||||
|
||||
gaokun_ec_psy_multi_read(ecbat->ec, EC_BAT_VENDOR,
|
||||
sizeof(ecbat->battery_vendor) - 1,
|
||||
ecbat->battery_vendor);
|
||||
|
||||
gaokun_ec_psy_multi_read(ecbat->ec, EC_BAT_MODEL,
|
||||
sizeof(ecbat->battery_model) - 1,
|
||||
ecbat->battery_model);
|
||||
|
||||
ecbat->battery_model[14] = 'A'; /* FIX UP */
|
||||
}
|
||||
|
||||
static int gaokun_psy_get_bat_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
u8 buf[GAOKUN_SMART_CHARGE_DATA_SIZE];
|
||||
int ret;
|
||||
|
||||
if (gaokun_psy_bat_present(ecbat))
|
||||
gaokun_psy_get_bat_status(ecbat);
|
||||
else if (psp != POWER_SUPPLY_PROP_PRESENT)
|
||||
return -ENODEV;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = ecbat->charge_now;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = ecbat->bat_present;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
||||
val->intval = le16_to_cpu(ecbat->info.cycle_count);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
val->intval = le16_to_cpu(ecbat->info.design_voltage) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = le16_to_cpu(ecbat->status.voltage_now) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
val->intval = (s16)le16_to_cpu(ecbat->status.rate_now) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
val->intval = le16_to_cpu(ecbat->info.design_capacity) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
val->intval = le16_to_cpu(ecbat->status.full_capacity) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
val->intval = le16_to_cpu(ecbat->status.capacity_now) * MILLI_TO_MICRO;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
ret = gaokun_ec_psy_get_smart_charge(ecbat->ec, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD)
|
||||
val->intval = buf[SMART_CHARGE_START];
|
||||
else
|
||||
val->intval = buf[SMART_CHARGE_END];
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = le16_to_cpu(ecbat->status.percentage_now);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = ecbat->battery_model;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = ecbat->battery_vendor;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||
val->strval = ecbat->battery_serial;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gaokun_psy_set_bat_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
u8 buf[GAOKUN_SMART_CHARGE_DATA_SIZE];
|
||||
int ret;
|
||||
|
||||
if (!gaokun_psy_bat_present(ecbat))
|
||||
return -ENODEV;
|
||||
|
||||
ret = gaokun_ec_psy_get_smart_charge(ecbat->ec, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (psp) {
|
||||
/*
|
||||
* Resetting another thershold makes single thersold setting more likely
|
||||
* to succeed. But setting start = end makes thing strange(failure).
|
||||
*/
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
|
||||
buf[SMART_CHARGE_START] = val->intval;
|
||||
if (buf[SMART_CHARGE_START] > buf[SMART_CHARGE_END])
|
||||
buf[SMART_CHARGE_END] = buf[SMART_CHARGE_START] + 1;
|
||||
return gaokun_ec_psy_set_smart_charge(ecbat->ec, buf);
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
buf[SMART_CHARGE_END] = val->intval;
|
||||
if (buf[SMART_CHARGE_END] < buf[SMART_CHARGE_START])
|
||||
buf[SMART_CHARGE_START] = buf[SMART_CHARGE_END] - 1;
|
||||
return gaokun_ec_psy_set_smart_charge(ecbat->ec, buf);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gaokun_psy_is_bat_property_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD ||
|
||||
psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
|
||||
}
|
||||
|
||||
static enum power_supply_property gaokun_psy_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc gaokun_psy_bat_desc = {
|
||||
.name = "gaokun-ec-battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.get_property = gaokun_psy_get_bat_property,
|
||||
.set_property = gaokun_psy_set_bat_property,
|
||||
.property_is_writeable = gaokun_psy_is_bat_property_writeable,
|
||||
.properties = gaokun_psy_bat_props,
|
||||
.num_properties = ARRAY_SIZE(gaokun_psy_bat_props),
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Sysfs */
|
||||
|
||||
/*
|
||||
* Note that, HUAWEI calls them SBAC/GBAC and SBCM/GBCM in DSDT, they are likely
|
||||
* Set/Get Battery Adaptive Charging and Set/Get Battery Charging Mode.
|
||||
*/
|
||||
|
||||
/* battery adaptive charge */
|
||||
static ssize_t battery_adaptive_charge_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
bool on;
|
||||
|
||||
ret = gaokun_ec_psy_get_smart_charge_enable(ecbat->ec, &on);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", on);
|
||||
}
|
||||
|
||||
static ssize_t battery_adaptive_charge_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
bool on;
|
||||
|
||||
if (kstrtobool(buf, &on))
|
||||
return -EINVAL;
|
||||
|
||||
ret = gaokun_ec_psy_set_smart_charge_enable(ecbat->ec, on);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(battery_adaptive_charge);
|
||||
|
||||
static inline int get_charge_delay(u8 buf[GAOKUN_SMART_CHARGE_DATA_SIZE])
|
||||
{
|
||||
return buf[SMART_CHARGE_MODE] == NO_DELAY_MODE ? 0 : buf[SMART_CHARGE_DELAY];
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_charge_delay(u8 buf[GAOKUN_SMART_CHARGE_DATA_SIZE], u8 delay)
|
||||
{
|
||||
if (delay) {
|
||||
buf[SMART_CHARGE_DELAY] = delay;
|
||||
buf[SMART_CHARGE_MODE] = DELAY_MODE;
|
||||
} else {
|
||||
/* No writing zero, there is a specific mode for it. */
|
||||
buf[SMART_CHARGE_MODE] = NO_DELAY_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Smart charge */
|
||||
static ssize_t smart_charge_delay_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
u8 bf[GAOKUN_SMART_CHARGE_DATA_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = gaokun_ec_psy_get_smart_charge(ecbat->ec, bf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", get_charge_delay(bf));
|
||||
}
|
||||
|
||||
static ssize_t smart_charge_delay_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct gaokun_psy *ecbat = power_supply_get_drvdata(psy);
|
||||
u8 bf[GAOKUN_SMART_CHARGE_DATA_SIZE];
|
||||
u8 delay;
|
||||
int ret;
|
||||
|
||||
if (kstrtou8(buf, 10, &delay))
|
||||
return -EINVAL;
|
||||
|
||||
ret = gaokun_ec_psy_get_smart_charge(ecbat->ec, bf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_charge_delay(bf, delay);
|
||||
|
||||
ret = gaokun_ec_psy_set_smart_charge(ecbat->ec, bf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(smart_charge_delay);
|
||||
|
||||
static struct attribute *gaokun_psy_features_attrs[] = {
|
||||
&dev_attr_battery_adaptive_charge.attr,
|
||||
&dev_attr_smart_charge_delay.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(gaokun_psy_features);
|
||||
|
||||
static int gaokun_psy_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct gaokun_psy *ecbat = container_of(nb, struct gaokun_psy, nb);
|
||||
|
||||
switch (action) {
|
||||
case EC_EVENT_BAT_A2:
|
||||
case EC_EVENT_BAT_B1:
|
||||
gaokun_psy_get_bat_info(ecbat);
|
||||
return NOTIFY_OK;
|
||||
|
||||
case EC_EVENT_BAT_A0:
|
||||
gaokun_psy_get_adp_status(ecbat);
|
||||
power_supply_changed(ecbat->adp_psy);
|
||||
msleep(10);
|
||||
fallthrough;
|
||||
|
||||
case EC_EVENT_BAT_A1:
|
||||
case EC_EVENT_BAT_A3:
|
||||
if (action == EC_EVENT_BAT_A3) {
|
||||
gaokun_psy_get_bat_info(ecbat);
|
||||
msleep(100);
|
||||
}
|
||||
gaokun_psy_get_bat_status(ecbat);
|
||||
power_supply_changed(ecbat->bat_psy);
|
||||
return NOTIFY_OK;
|
||||
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
static int gaokun_psy_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct gaokun_ec *ec = adev->dev.platform_data;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct device *dev = &adev->dev;
|
||||
struct gaokun_psy *ecbat;
|
||||
|
||||
ecbat = devm_kzalloc(&adev->dev, sizeof(*ecbat), GFP_KERNEL);
|
||||
if (!ecbat)
|
||||
return -ENOMEM;
|
||||
|
||||
ecbat->ec = ec;
|
||||
ecbat->dev = dev;
|
||||
ecbat->nb.notifier_call = gaokun_psy_notify;
|
||||
|
||||
auxiliary_set_drvdata(adev, ecbat);
|
||||
|
||||
psy_cfg.drv_data = ecbat;
|
||||
ecbat->adp_psy = devm_power_supply_register(dev, &gaokun_psy_adp_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(ecbat->adp_psy))
|
||||
return dev_err_probe(dev, PTR_ERR(ecbat->adp_psy),
|
||||
"Failed to register AC power supply\n");
|
||||
|
||||
psy_cfg.supplied_to = (char **)&gaokun_psy_bat_desc.name;
|
||||
psy_cfg.num_supplicants = 1;
|
||||
psy_cfg.no_wakeup_source = true;
|
||||
psy_cfg.attr_grp = gaokun_psy_features_groups;
|
||||
ecbat->bat_psy = devm_power_supply_register(dev, &gaokun_psy_bat_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(ecbat->bat_psy))
|
||||
return dev_err_probe(dev, PTR_ERR(ecbat->bat_psy),
|
||||
"Failed to register battery power supply\n");
|
||||
gaokun_psy_init(ecbat);
|
||||
|
||||
return gaokun_ec_register_notify(ec, &ecbat->nb);
|
||||
}
|
||||
|
||||
static void gaokun_psy_remove(struct auxiliary_device *adev)
|
||||
{
|
||||
struct gaokun_psy *ecbat = auxiliary_get_drvdata(adev);
|
||||
|
||||
gaokun_ec_unregister_notify(ecbat->ec, &ecbat->nb);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id gaokun_psy_id_table[] = {
|
||||
{ .name = GAOKUN_MOD_NAME "." GAOKUN_DEV_PSY, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, gaokun_psy_id_table);
|
||||
|
||||
static struct auxiliary_driver gaokun_psy_driver = {
|
||||
.name = GAOKUN_DEV_PSY,
|
||||
.id_table = gaokun_psy_id_table,
|
||||
.probe = gaokun_psy_probe,
|
||||
.remove = gaokun_psy_remove,
|
||||
};
|
||||
|
||||
module_auxiliary_driver(gaokun_psy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HUAWEI Matebook E Go psy driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -410,8 +410,9 @@ static int max17040_get_property(struct power_supply *psy,
|
|||
if (!chip->channel_temp)
|
||||
return -ENODATA;
|
||||
|
||||
iio_read_channel_processed_scale(chip->channel_temp,
|
||||
&val->intval, 10);
|
||||
iio_read_channel_processed(chip->channel_temp, &val->intval);
|
||||
val->intval /= 100; /* Convert from milli- to deci-degree */
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -545,20 +545,28 @@ static int max77705_charger_probe(struct i2c_client *i2c)
|
|||
return dev_err_probe(dev, ret, "failed to add irq chip\n");
|
||||
|
||||
chg->wqueue = create_singlethread_workqueue(dev_name(dev));
|
||||
if (IS_ERR(chg->wqueue))
|
||||
return dev_err_probe(dev, PTR_ERR(chg->wqueue), "failed to create workqueue\n");
|
||||
if (!chg->wqueue)
|
||||
return dev_err_probe(dev, -ENOMEM, "failed to create workqueue\n");
|
||||
|
||||
ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to initialize interrupt work\n");
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to initialize interrupt work\n");
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
max77705_charger_initialize(chg);
|
||||
|
||||
ret = max77705_charger_enable(chg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to enable charge\n");
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to enable charge\n");
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(dev, max77705_charger_disable, chg);
|
||||
|
||||
destroy_wq:
|
||||
destroy_workqueue(chg->wqueue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id max77705_charger_of_match[] = {
|
||||
|
|
|
|||
752
drivers/power/supply/max8971_charger.c
Normal file
752
drivers/power/supply/max8971_charger.c
Normal file
|
|
@ -0,0 +1,752 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MAX8971_REG_CHGINT 0x0f
|
||||
#define MAX8971_REG_CHG_RST BIT(0)
|
||||
#define MAX8971_REG_CHGINT_MASK 0x01
|
||||
#define MAX8971_AICL_MASK BIT(7)
|
||||
#define MAX8971_REG_CHG_STAT 0x02
|
||||
#define MAX8971_CHG_MASK BIT(3)
|
||||
#define MAX8971_REG_DETAILS1 0x03
|
||||
#define MAX8971_REG_DETAILS2 0x04
|
||||
#define MAX8971_REG_CHGCNTL1 0x05
|
||||
#define MAX8971_REG_FCHGCRNT 0x06
|
||||
#define MAX8971_REG_DCCRNT 0x07
|
||||
#define MAX8971_CHGRSTRT_MASK BIT(6)
|
||||
#define MAX8971_REG_TOPOFF 0x08
|
||||
#define MAX8971_REG_TEMPREG 0x09
|
||||
#define MAX8971_REG_PROTCMD 0x0a
|
||||
#define MAX8971_CHGPROT_LOCKED 0x00
|
||||
#define MAX8971_CHGPROT_UNLOCKED 0x03
|
||||
|
||||
#define MAX8971_FCHGT_DEFAULT 2
|
||||
#define MAX8971_TOPOFFT_DEFAULT 3
|
||||
|
||||
static const char *max8971_manufacturer = "Maxim Integrated";
|
||||
static const char *max8971_model = "MAX8971";
|
||||
|
||||
enum max8971_charging_state {
|
||||
MAX8971_CHARGING_DEAD_BATTERY,
|
||||
MAX8971_CHARGING_PREQUALIFICATION,
|
||||
MAX8971_CHARGING_FAST_CONST_CURRENT,
|
||||
MAX8971_CHARGING_FAST_CONST_VOLTAGE,
|
||||
MAX8971_CHARGING_TOP_OFF,
|
||||
MAX8971_CHARGING_DONE,
|
||||
MAX8971_CHARGING_TIMER_FAULT,
|
||||
MAX8971_CHARGING_SUSPENDED_THERMAL,
|
||||
MAX8971_CHARGING_OFF,
|
||||
MAX8971_CHARGING_THERMAL_LOOP,
|
||||
};
|
||||
|
||||
enum max8971_health_state {
|
||||
MAX8971_HEALTH_UNKNOWN,
|
||||
MAX8971_HEALTH_COLD,
|
||||
MAX8971_HEALTH_COOL,
|
||||
MAX8971_HEALTH_WARM,
|
||||
MAX8971_HEALTH_HOT,
|
||||
MAX8971_HEALTH_OVERHEAT,
|
||||
};
|
||||
|
||||
/* Fast-Charge current limit, 250..1550 mA, 50 mA steps */
|
||||
#define MAX8971_CHG_CC_STEP 50000U
|
||||
#define MAX8971_CHG_CC_MIN 250000U
|
||||
#define MAX8971_CHG_CC_MAX 1550000U
|
||||
|
||||
/* Input current limit, 250..1500 mA, 25 mA steps */
|
||||
#define MAX8971_DCILMT_STEP 25000U
|
||||
#define MAX8971_DCILMT_MIN 250000U
|
||||
#define MAX8971_DCILMT_MAX 1500000U
|
||||
|
||||
enum max8971_field_idx {
|
||||
THM_DTLS, /* DETAILS1 */
|
||||
BAT_DTLS, CHG_DTLS, /* DETAILS2 */
|
||||
CHG_CC, FCHG_T, /* FCHGCRNT */
|
||||
DCI_LMT, /* DCCRNT */
|
||||
TOPOFF_T, TOPOFF_S, /* TOPOFF */
|
||||
CPROT, /* PROTCMD */
|
||||
MAX8971_N_REGMAP_FIELDS
|
||||
};
|
||||
|
||||
static const struct reg_field max8971_reg_field[MAX8971_N_REGMAP_FIELDS] = {
|
||||
[THM_DTLS] = REG_FIELD(MAX8971_REG_DETAILS1, 0, 2),
|
||||
[BAT_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 4, 5),
|
||||
[CHG_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 0, 3),
|
||||
[CHG_CC] = REG_FIELD(MAX8971_REG_FCHGCRNT, 0, 4),
|
||||
[FCHG_T] = REG_FIELD(MAX8971_REG_FCHGCRNT, 5, 7),
|
||||
[DCI_LMT] = REG_FIELD(MAX8971_REG_DCCRNT, 0, 5),
|
||||
[TOPOFF_T] = REG_FIELD(MAX8971_REG_TOPOFF, 5, 7),
|
||||
[TOPOFF_S] = REG_FIELD(MAX8971_REG_TOPOFF, 2, 3),
|
||||
[CPROT] = REG_FIELD(MAX8971_REG_PROTCMD, 2, 3),
|
||||
};
|
||||
|
||||
static const struct regmap_config max8971_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = MAX8971_REG_CHGINT,
|
||||
};
|
||||
|
||||
struct max8971_data {
|
||||
struct device *dev;
|
||||
struct power_supply *psy_mains;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block extcon_nb;
|
||||
struct delayed_work extcon_work;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *rfield[MAX8971_N_REGMAP_FIELDS];
|
||||
|
||||
enum power_supply_usb_type usb_type;
|
||||
|
||||
u32 fchgt;
|
||||
u32 tofft;
|
||||
u32 toffs;
|
||||
|
||||
bool present;
|
||||
};
|
||||
|
||||
static int max8971_get_status(struct max8971_data *priv, int *val)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[CHG_DTLS], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (regval) {
|
||||
case MAX8971_CHARGING_DEAD_BATTERY:
|
||||
case MAX8971_CHARGING_PREQUALIFICATION:
|
||||
case MAX8971_CHARGING_FAST_CONST_CURRENT:
|
||||
case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
|
||||
case MAX8971_CHARGING_TOP_OFF:
|
||||
case MAX8971_CHARGING_THERMAL_LOOP:
|
||||
*val = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case MAX8971_CHARGING_DONE:
|
||||
*val = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
case MAX8971_CHARGING_TIMER_FAULT:
|
||||
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
case MAX8971_CHARGING_OFF:
|
||||
case MAX8971_CHARGING_SUSPENDED_THERMAL:
|
||||
*val = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
default:
|
||||
*val = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8971_get_charge_type(struct max8971_data *priv, int *val)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[CHG_DTLS], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (regval) {
|
||||
case MAX8971_CHARGING_DEAD_BATTERY:
|
||||
case MAX8971_CHARGING_PREQUALIFICATION:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
break;
|
||||
case MAX8971_CHARGING_FAST_CONST_CURRENT:
|
||||
case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
break;
|
||||
case MAX8971_CHARGING_TOP_OFF:
|
||||
case MAX8971_CHARGING_THERMAL_LOOP:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||
break;
|
||||
case MAX8971_CHARGING_DONE:
|
||||
case MAX8971_CHARGING_TIMER_FAULT:
|
||||
case MAX8971_CHARGING_SUSPENDED_THERMAL:
|
||||
case MAX8971_CHARGING_OFF:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
default:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8971_get_health(struct max8971_data *priv, int *val)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[THM_DTLS], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (regval) {
|
||||
case MAX8971_HEALTH_COLD:
|
||||
*val = POWER_SUPPLY_HEALTH_COLD;
|
||||
break;
|
||||
case MAX8971_HEALTH_COOL:
|
||||
*val = POWER_SUPPLY_HEALTH_COOL;
|
||||
break;
|
||||
case MAX8971_HEALTH_WARM:
|
||||
*val = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case MAX8971_HEALTH_HOT:
|
||||
*val = POWER_SUPPLY_HEALTH_HOT;
|
||||
break;
|
||||
case MAX8971_HEALTH_OVERHEAT:
|
||||
*val = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
break;
|
||||
case MAX8971_HEALTH_UNKNOWN:
|
||||
default:
|
||||
*val = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8971_get_online(struct max8971_data *priv, int *val)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_read(priv->regmap, MAX8971_REG_CHG_STAT, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (priv->present)
|
||||
/* CHG_OK bit is 0 when charger is online */
|
||||
*val = !(regval & MAX8971_CHG_MASK);
|
||||
else
|
||||
*val = priv->present;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8971_get_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
|
||||
u32 clamp_min, u32 clamp_max, u32 mult, int *val)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[fidx], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*val = clamp_val(regval * mult, clamp_min, clamp_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8971_set_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
|
||||
u32 clamp_min, u32 clamp_max, u32 div, int val)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = clamp_val(val, clamp_min, clamp_max) / div;
|
||||
|
||||
return regmap_field_write(priv->rfield[fidx], regval);
|
||||
}
|
||||
|
||||
static int max8971_get_property(struct power_supply *psy, enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
int err = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
err = max8971_get_status(priv, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
err = max8971_get_charge_type(priv, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
val->intval = priv->usb_type;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
err = max8971_get_health(priv, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
err = max8971_get_online(priv, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = priv->present;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
||||
val->intval = MAX8971_CHG_CC_MAX;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
err = max8971_get_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
|
||||
MAX8971_CHG_CC_STEP, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
err = max8971_get_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
|
||||
MAX8971_DCILMT_STEP, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = max8971_model;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = max8971_manufacturer;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int max8971_set_property(struct power_supply *psy, enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
int err = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
err = max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
|
||||
MAX8971_CHG_CC_STEP, val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
err = max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
|
||||
MAX8971_DCILMT_STEP, val->intval);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
static int max8971_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static enum power_supply_property max8971_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc max8971_charger_desc = {
|
||||
.name = "max8971-charger",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.usb_types = BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_SDP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_CDP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_ACA),
|
||||
.properties = max8971_properties,
|
||||
.num_properties = ARRAY_SIZE(max8971_properties),
|
||||
.get_property = max8971_get_property,
|
||||
.set_property = max8971_set_property,
|
||||
.property_is_writeable = max8971_property_is_writeable,
|
||||
};
|
||||
|
||||
static void max8971_update_config(struct max8971_data *priv)
|
||||
{
|
||||
regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);
|
||||
|
||||
if (priv->fchgt != MAX8971_FCHGT_DEFAULT)
|
||||
regmap_field_write(priv->rfield[FCHG_T], priv->fchgt);
|
||||
|
||||
regmap_write_bits(priv->regmap, MAX8971_REG_DCCRNT, MAX8971_CHGRSTRT_MASK,
|
||||
MAX8971_CHGRSTRT_MASK);
|
||||
|
||||
if (priv->tofft != MAX8971_TOPOFFT_DEFAULT)
|
||||
regmap_field_write(priv->rfield[TOPOFF_T], priv->tofft);
|
||||
|
||||
if (priv->toffs)
|
||||
regmap_field_write(priv->rfield[TOPOFF_S], priv->toffs);
|
||||
|
||||
regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
|
||||
}
|
||||
|
||||
static ssize_t fast_charge_timer_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[FCHG_T], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (regval) {
|
||||
case 0x1 ... 0x7:
|
||||
/* Time is off by 3 hours comparing to value */
|
||||
regval += 3;
|
||||
break;
|
||||
case 0x0:
|
||||
default:
|
||||
regval = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%u\n", regval);
|
||||
}
|
||||
|
||||
static ssize_t fast_charge_timer_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
unsigned long hours;
|
||||
int val, err;
|
||||
|
||||
err = kstrtoul(buf, 10, &hours);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = hours - 3;
|
||||
if (val <= 0 || val > 7)
|
||||
priv->fchgt = 0;
|
||||
else
|
||||
priv->fchgt = val;
|
||||
|
||||
max8971_update_config(priv);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t top_off_threshold_current_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
u32 regval, val;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[TOPOFF_S], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* 50uA start with 50uA step */
|
||||
val = regval * 50 + 50;
|
||||
val *= 1000;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t top_off_threshold_current_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
unsigned long uamp;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &uamp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (uamp < 50000 || uamp > 200000)
|
||||
return -EINVAL;
|
||||
|
||||
priv->toffs = uamp / 50000 - 1;
|
||||
|
||||
max8971_update_config(priv);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t top_off_timer_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = regmap_field_read(priv->rfield[TOPOFF_T], ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* 10 min intervals */
|
||||
regval *= 10;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", regval);
|
||||
}
|
||||
|
||||
static ssize_t top_off_timer_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct max8971_data *priv = power_supply_get_drvdata(psy);
|
||||
unsigned long minutes;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &minutes);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (minutes > 70)
|
||||
return -EINVAL;
|
||||
|
||||
priv->tofft = minutes / 10;
|
||||
|
||||
max8971_update_config(priv);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(fast_charge_timer);
|
||||
static DEVICE_ATTR_RW(top_off_threshold_current);
|
||||
static DEVICE_ATTR_RW(top_off_timer);
|
||||
|
||||
static struct attribute *max8971_attrs[] = {
|
||||
&dev_attr_fast_charge_timer.attr,
|
||||
&dev_attr_top_off_threshold_current.attr,
|
||||
&dev_attr_top_off_timer.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(max8971);
|
||||
|
||||
static void max8971_extcon_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct max8971_data *priv =
|
||||
container_of(work, struct max8971_data, extcon_work.work);
|
||||
struct device *dev = priv->dev;
|
||||
struct extcon_dev *edev = priv->edev;
|
||||
u32 chgcc, dcilmt;
|
||||
|
||||
if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
|
||||
dev_dbg(dev, "USB SDP charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
chgcc = 500000;
|
||||
dcilmt = 500000;
|
||||
} else if (extcon_get_state(edev, EXTCON_USB) > 0) {
|
||||
dev_dbg(dev, "USB charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
chgcc = 500000;
|
||||
dcilmt = 500000;
|
||||
} else if (extcon_get_state(edev, EXTCON_DISP_MHL) > 0) {
|
||||
dev_dbg(dev, "MHL plug is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
chgcc = 500000;
|
||||
dcilmt = 500000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
|
||||
dev_dbg(dev, "USB DCP charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
|
||||
chgcc = 900000;
|
||||
dcilmt = 1200000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_FAST) > 0) {
|
||||
dev_dbg(dev, "USB FAST charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
|
||||
chgcc = 900000;
|
||||
dcilmt = 1200000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_SLOW) > 0) {
|
||||
dev_dbg(dev, "USB SLOW charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
|
||||
chgcc = 900000;
|
||||
dcilmt = 1200000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
|
||||
dev_dbg(dev, "USB CDP charger is connected\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
|
||||
chgcc = 900000;
|
||||
dcilmt = 1200000;
|
||||
} else {
|
||||
dev_dbg(dev, "USB state is unknown\n");
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);
|
||||
|
||||
max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
|
||||
MAX8971_CHG_CC_STEP, chgcc);
|
||||
max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
|
||||
MAX8971_DCILMT_STEP, dcilmt);
|
||||
|
||||
regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
|
||||
}
|
||||
|
||||
static int extcon_get_charger_type(struct notifier_block *nb,
|
||||
unsigned long state, void *data)
|
||||
{
|
||||
struct max8971_data *priv =
|
||||
container_of(nb, struct max8971_data, extcon_nb);
|
||||
schedule_delayed_work(&priv->extcon_work, 0);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static irqreturn_t max8971_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct max8971_data *priv = dev_id;
|
||||
struct device *dev = priv->dev;
|
||||
int err, state;
|
||||
|
||||
err = regmap_read(priv->regmap, MAX8971_REG_CHGINT, &state);
|
||||
if (err)
|
||||
dev_err(dev, "interrupt reg read failed %d\n", err);
|
||||
|
||||
err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK,
|
||||
MAX8971_AICL_MASK, MAX8971_AICL_MASK);
|
||||
if (err)
|
||||
dev_err(dev, "failed to mask IRQ\n");
|
||||
|
||||
/* set presence prop */
|
||||
priv->present = state & MAX8971_REG_CHG_RST;
|
||||
|
||||
/* on every plug chip resets to default */
|
||||
if (priv->present)
|
||||
max8971_update_config(priv);
|
||||
|
||||
/* update supply status */
|
||||
power_supply_changed(priv->psy_mains);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max8971_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct max8971_data *priv;
|
||||
struct device_node *extcon;
|
||||
struct power_supply_config cfg = { };
|
||||
int err, i;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||||
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(client, &max8971_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->regmap), "cannot allocate regmap\n");
|
||||
|
||||
for (i = 0; i < MAX8971_N_REGMAP_FIELDS; i++) {
|
||||
priv->rfield[i] = devm_regmap_field_alloc(dev, priv->regmap, max8971_reg_field[i]);
|
||||
if (IS_ERR(priv->rfield[i]))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->rfield[i]),
|
||||
"cannot allocate regmap field\n");
|
||||
}
|
||||
|
||||
cfg.attr_grp = max8971_groups;
|
||||
cfg.drv_data = priv;
|
||||
cfg.fwnode = dev_fwnode(dev);
|
||||
|
||||
priv->psy_mains = devm_power_supply_register(dev, &max8971_charger_desc, &cfg);
|
||||
if (IS_ERR(priv->psy_mains))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->psy_mains),
|
||||
"failed to register mains supply\n");
|
||||
|
||||
err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK, MAX8971_AICL_MASK,
|
||||
MAX8971_AICL_MASK);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to mask IRQ\n");
|
||||
|
||||
err = devm_request_threaded_irq(dev, client->irq, NULL, &max8971_interrupt,
|
||||
IRQF_ONESHOT | IRQF_SHARED, client->name, priv);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to register IRQ %d\n", client->irq);
|
||||
|
||||
extcon = of_graph_get_remote_node(dev->of_node, -1, -1);
|
||||
if (!extcon)
|
||||
return 0;
|
||||
|
||||
priv->edev = extcon_find_edev_by_node(extcon);
|
||||
of_node_put(extcon);
|
||||
if (IS_ERR(priv->edev))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->edev), "failed to find extcon\n");
|
||||
|
||||
err = devm_delayed_work_autocancel(dev, &priv->extcon_work,
|
||||
max8971_extcon_evt_worker);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to add extcon evt stop action\n");
|
||||
|
||||
priv->extcon_nb.notifier_call = extcon_get_charger_type;
|
||||
|
||||
err = devm_extcon_register_notifier_all(dev, priv->edev, &priv->extcon_nb);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "failed to register notifier\n");
|
||||
|
||||
/* Initial configuration work with 1 sec delay */
|
||||
schedule_delayed_work(&priv->extcon_work, msecs_to_jiffies(1000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused max8971_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct max8971_data *priv = i2c_get_clientdata(client);
|
||||
|
||||
irq_wake_thread(client->irq, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max8971_pm_ops, NULL, max8971_resume);
|
||||
|
||||
static const struct of_device_id max8971_match_ids[] = {
|
||||
{ .compatible = "maxim,max8971" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max8971_match_ids);
|
||||
|
||||
static const struct i2c_device_id max8971_i2c_id[] = {
|
||||
{ "max8971" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max8971_i2c_id);
|
||||
|
||||
static struct i2c_driver max8971_driver = {
|
||||
.driver = {
|
||||
.name = "max8971-charger",
|
||||
.of_match_table = max8971_match_ids,
|
||||
.pm = &max8971_pm_ops,
|
||||
},
|
||||
.probe = max8971_probe,
|
||||
.id_table = max8971_i2c_id,
|
||||
};
|
||||
module_i2c_driver(max8971_driver);
|
||||
|
||||
MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
|
||||
MODULE_DESCRIPTION("MAX8971 Charger Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -321,6 +321,27 @@ static ssize_t power_supply_show_charge_behaviour(struct device *dev,
|
|||
value->intval, buf);
|
||||
}
|
||||
|
||||
static ssize_t power_supply_show_charge_types(struct device *dev,
|
||||
struct power_supply *psy,
|
||||
enum power_supply_charge_type current_type,
|
||||
char *buf)
|
||||
{
|
||||
struct power_supply_ext_registration *reg;
|
||||
|
||||
scoped_guard(rwsem_read, &psy->extensions_sem) {
|
||||
power_supply_for_each_extension(reg, psy) {
|
||||
if (power_supply_ext_has_property(reg->ext,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPES))
|
||||
return power_supply_charge_types_show(dev,
|
||||
reg->ext->charge_types,
|
||||
current_type, buf);
|
||||
}
|
||||
}
|
||||
|
||||
return power_supply_charge_types_show(dev, psy->desc->charge_types,
|
||||
current_type, buf);
|
||||
}
|
||||
|
||||
static ssize_t power_supply_format_property(struct device *dev,
|
||||
bool uevent,
|
||||
struct device_attribute *attr,
|
||||
|
|
@ -365,7 +386,7 @@ static ssize_t power_supply_format_property(struct device *dev,
|
|||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
if (uevent) /* no possible values in uevents */
|
||||
goto default_format;
|
||||
ret = power_supply_charge_types_show(dev, psy->desc->charge_types,
|
||||
ret = power_supply_show_charge_types(dev, psy,
|
||||
value.intval, buf);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||
|
|
|
|||
|
|
@ -1088,7 +1088,7 @@ static int rk817_charger_probe(struct platform_device *pdev)
|
|||
rk817_bat_calib_vol(charger);
|
||||
|
||||
pscfg.drv_data = charger;
|
||||
pscfg.fwnode = node ? &node->fwnode : NULL;
|
||||
pscfg.fwnode = &node->fwnode;
|
||||
|
||||
/*
|
||||
* Get sample resistor value. Note only values of 10000 or 20000
|
||||
|
|
|
|||
|
|
@ -192,12 +192,12 @@ static const struct reg_field rt9471_reg_fields[F_MAX_FIELDS] = {
|
|||
};
|
||||
|
||||
static const struct linear_range rt9471_chg_ranges[RT9471_MAX_RANGES] = {
|
||||
[RT9471_RANGE_AICR] = { .min = 50000, .min_sel = 1, .max_sel = 63, .step = 50000 },
|
||||
[RT9471_RANGE_MIVR] = { .min = 3900000, .min_sel = 0, .max_sel = 15, .step = 100000 },
|
||||
[RT9471_RANGE_IPRE] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 },
|
||||
[RT9471_RANGE_VCHG] = { .min = 3900000, .min_sel = 0, .max_sel = 80, .step = 10000 },
|
||||
[RT9471_RANGE_ICHG] = { .min = 0, .min_sel = 0, .max_sel = 63, .step = 50000 },
|
||||
[RT9471_RANGE_IEOC] = { .min = 50000, .min_sel = 0, .max_sel = 15, .step = 50000 },
|
||||
[RT9471_RANGE_AICR] = LINEAR_RANGE(50000, 1, 63, 50000),
|
||||
[RT9471_RANGE_MIVR] = LINEAR_RANGE(3900000, 0, 15, 100000),
|
||||
[RT9471_RANGE_IPRE] = LINEAR_RANGE(50000, 0, 15, 50000),
|
||||
[RT9471_RANGE_VCHG] = LINEAR_RANGE(3900000, 0, 80, 10000),
|
||||
[RT9471_RANGE_ICHG] = LINEAR_RANGE(0, 0, 63, 50000),
|
||||
[RT9471_RANGE_IEOC] = LINEAR_RANGE(50000, 0, 15, 50000),
|
||||
};
|
||||
|
||||
static int rt9471_set_value_by_field_range(struct rt9471_chip *chip,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ static int battery_charge_counter = -1000;
|
|||
static int battery_current = -1600;
|
||||
static enum power_supply_charge_behaviour battery_charge_behaviour =
|
||||
POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
||||
static enum power_supply_charge_type battery_charge_types =
|
||||
POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||
static bool battery_extension;
|
||||
|
||||
static bool module_initialized;
|
||||
|
|
@ -87,7 +89,7 @@ static int test_power_get_battery_property(struct power_supply *psy,
|
|||
val->intval = battery_status;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
val->intval = battery_charge_types;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = battery_health;
|
||||
|
|
@ -129,6 +131,9 @@ static int test_power_get_battery_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
||||
val->intval = battery_charge_behaviour;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
val->intval = battery_charge_types;
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: some properties deliberately report errors.\n",
|
||||
__func__);
|
||||
|
|
@ -140,7 +145,7 @@ static int test_power_get_battery_property(struct power_supply *psy,
|
|||
static int test_power_battery_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
return psp == POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR;
|
||||
return psp == POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR || psp == POWER_SUPPLY_PROP_CHARGE_TYPES;
|
||||
}
|
||||
|
||||
static int test_power_set_battery_property(struct power_supply *psy,
|
||||
|
|
@ -156,6 +161,14 @@ static int test_power_set_battery_property(struct power_supply *psy,
|
|||
}
|
||||
battery_charge_behaviour = val->intval;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPES:
|
||||
if (val->intval < 0 ||
|
||||
val->intval >= BITS_PER_TYPE(typeof(psy->desc->charge_types)) ||
|
||||
!(BIT(val->intval) & psy->desc->charge_types)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
battery_charge_types = val->intval;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -188,6 +201,7 @@ static enum power_supply_property test_power_battery_props[] = {
|
|||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPES,
|
||||
};
|
||||
|
||||
static char *test_power_ac_supplied_to[] = {
|
||||
|
|
@ -215,6 +229,8 @@ static const struct power_supply_desc test_power_desc[] = {
|
|||
.charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO)
|
||||
| BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)
|
||||
| BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE),
|
||||
.charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD)
|
||||
| BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)
|
||||
},
|
||||
[TEST_USB] = {
|
||||
.name = "test_usb",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ static int wm831x_wall_get_prop(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm831x_wall_props[] = {
|
||||
static const enum power_supply_property wm831x_wall_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
};
|
||||
|
|
@ -120,7 +120,7 @@ static int wm831x_usb_get_prop(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm831x_usb_props[] = {
|
||||
static const enum power_supply_property wm831x_usb_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
};
|
||||
|
|
@ -171,21 +171,21 @@ struct chg_map {
|
|||
int reg_val;
|
||||
};
|
||||
|
||||
static struct chg_map trickle_ilims[] = {
|
||||
static const struct chg_map trickle_ilims[] = {
|
||||
{ 50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
|
||||
{ 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
|
||||
{ 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
|
||||
{ 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
|
||||
};
|
||||
|
||||
static struct chg_map vsels[] = {
|
||||
static const struct chg_map vsels[] = {
|
||||
{ 4050, 0 << WM831X_CHG_VSEL_SHIFT },
|
||||
{ 4100, 1 << WM831X_CHG_VSEL_SHIFT },
|
||||
{ 4150, 2 << WM831X_CHG_VSEL_SHIFT },
|
||||
{ 4200, 3 << WM831X_CHG_VSEL_SHIFT },
|
||||
};
|
||||
|
||||
static struct chg_map fast_ilims[] = {
|
||||
static const struct chg_map fast_ilims[] = {
|
||||
{ 0, 0 << WM831X_CHG_FAST_ILIM_SHIFT },
|
||||
{ 50, 1 << WM831X_CHG_FAST_ILIM_SHIFT },
|
||||
{ 100, 2 << WM831X_CHG_FAST_ILIM_SHIFT },
|
||||
|
|
@ -204,7 +204,7 @@ static struct chg_map fast_ilims[] = {
|
|||
{ 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
|
||||
};
|
||||
|
||||
static struct chg_map eoc_iterms[] = {
|
||||
static const struct chg_map eoc_iterms[] = {
|
||||
{ 20, 0 << WM831X_CHG_ITERM_SHIFT },
|
||||
{ 30, 1 << WM831X_CHG_ITERM_SHIFT },
|
||||
{ 40, 2 << WM831X_CHG_ITERM_SHIFT },
|
||||
|
|
@ -215,7 +215,7 @@ static struct chg_map eoc_iterms[] = {
|
|||
{ 90, 7 << WM831X_CHG_ITERM_SHIFT },
|
||||
};
|
||||
|
||||
static struct chg_map chg_times[] = {
|
||||
static const struct chg_map chg_times[] = {
|
||||
{ 60, 0 << WM831X_CHG_TIME_SHIFT },
|
||||
{ 90, 1 << WM831X_CHG_TIME_SHIFT },
|
||||
{ 120, 2 << WM831X_CHG_TIME_SHIFT },
|
||||
|
|
@ -235,7 +235,7 @@ static struct chg_map chg_times[] = {
|
|||
};
|
||||
|
||||
static void wm831x_battery_apply_config(struct wm831x *wm831x,
|
||||
struct chg_map *map, int count, int val,
|
||||
const struct chg_map *map, int count, int val,
|
||||
int *reg, const char *name,
|
||||
const char *units)
|
||||
{
|
||||
|
|
@ -462,7 +462,7 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property wm831x_bat_props[] = {
|
||||
static const enum power_supply_property wm831x_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
|
|
@ -470,7 +470,7 @@ static enum power_supply_property wm831x_bat_props[] = {
|
|||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
};
|
||||
|
||||
static const char *wm831x_bat_irqs[] = {
|
||||
static const char * const wm831x_bat_irqs[] = {
|
||||
"BATT HOT",
|
||||
"BATT COLD",
|
||||
"BATT FAIL",
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ struct power_supply_desc {
|
|||
struct power_supply_ext {
|
||||
const char *const name;
|
||||
u8 charge_behaviours;
|
||||
u32 charge_types;
|
||||
const enum power_supply_property *properties;
|
||||
size_t num_properties;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user