mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
power supply and reset changes for the 7.3 series
* power-supply core - Add PbAc, NiZn, RAM, and ZnAr battery chemistry types - Create LED triggers based on properties instead of device type - Provide power_supply_get_system_batteries() for usage with USB-C - Add registration init callback for race-free device setup * power-supply drivers - new TI BQ25630 charger driver - new SG Micro sgm41542 charger driver - bq257xx: Add support for BQ25792 - max8903: add DC and USB input current-limit controls - max17042_battery: Initialize MAX17055 from battery info - sbs-battery: map newly introduced battery chemistries - drop extra error messages for IRQ request failures - lot's of driver removal race condition fixes - misc. small cleanups and fixes * reset drivers - add MCF5441x RCM power-on reason driver - misc. small cleanups and fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmqHhI8ACgkQ2O7X88g7 +prz8A//RwS840YNf/HU54EoG9daMtipcZ6RJrSoY0UiS4oYxERuOoP3azaj/B+l 6x7q8sp84E6Ejrrt1Hr6guKvRclVbgFJwagbqbzZ1l/EQvxgHPo2grHlSHSpqoPM NafNyFyrItuC9T02XkI1AnDcrXBIXZ4EXxIH2pe/da027lUGkAZO56oxmX5WDBRc OsUvCBBMOe2wlLnQErT7AfEFOic4H4Uz6j+CDwitHGqPcctpLR9JuBiB5QDFKaeY Nf3GDxRG1pahCCgs9ltOAHKx3UmS71xwfEWoig4h4r88OpV59Mm3fMhteTSt2nAl 5c5tkSwBpnlTdMJdJkrlR4bmGMGjAdd0+MkqBivWGev1NJMNo/QGoh0a7pVUFGSt lPgy0vA51+ZBGmcEY6lh4uOlJ0Pn9Sd04Ao/En+0fd0r+jlq3x1JebwqTeFt9pNd 5zXxl2UKpd6xaF6EZoV59AI/DCwNKxHpFfh1IMvBsfFAqe/ULrlr4v+AD+TCUMdr PLU/jdg9j8iIXo7s5U9Xymba5/cxFmy95u+fsVtjjFhHdSRMcKjwRS3mg0L5W+GN umy/2ewraTNsHCLZ7bS0/OL/rSv6nnL5DH/Jk02pkws2lpmVL5x+xeCK4UTJy6rA rMHa+Ex5/i+fW5n5XBgbDmRu/ihojcsVX5Xz/7/LqyuHvTlLWj4= =TxN7 -----END PGP SIGNATURE----- Merge tag 'for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: "Power-supply core: - Add PbAc, NiZn, RAM, and ZnAr battery chemistry types - Create LED triggers based on properties instead of device type - Provide power_supply_get_system_batteries() for usage with USB-C - Add registration init callback for race-free device setup Power-supply drivers: - new TI BQ25630 charger driver - new SG Micro sgm41542 charger driver - bq257xx: Add support for BQ25792 - max8903: add DC and USB input current-limit controls - max17042_battery: Initialize MAX17055 from battery info - sbs-battery: map newly introduced battery chemistries - drop extra error messages for IRQ request failures - lot's of driver removal race condition fixes - misc small cleanups and fixes Reset drivers: - add MCF5441x RCM power-on reason driver - misc small cleanups and fixes" * tag 'for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (115 commits) power: supply: bq27xxx: bq27z561: fix invalid AverageEnergy address power: supply: bq27xxx: bq28z610: fix invalid AverageEnergy address power: supply: bq27xxx: bq27520g4: fix REG_TTES address power: supply: max17040: synchronize work cancellation on suspend power: supply: lp8727: fix use-after-free in lp8727_release_irq() power: supply: bq256xx: drain usb_work before freeing the charger power: supply: qcom_battmgr: fix battery chemistry strncmp length power: supply: bd99954: Drop bad register fields power: supply: bd71828: Do not hide errors power: supply: bd71828: Drop duplicate power-supply property power: supply: bd71828: Fix current direction power: supply: bd71815: Fix temperature reading power: supply: add stubs for notifier registration helpers power: supply: ucs1002: fix use-after-free on remove power: supply: lp8788-charger: fix use-after-free on remove power: supply: ab8500_fg: fix use-after-free on remove power: supply: bq24257: fix use-after-free on remove power: supply: qcom_battmgr: fix use-after-free power: supply: max17040: drop incorrect I2C functionality check power: supply: charger-manager: register regulators before exposing sysfs ...
This commit is contained in:
commit
2be02a7c99
|
|
@ -525,7 +525,7 @@ Description:
|
|||
|
||||
Valid values:
|
||||
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe",
|
||||
"NiCd", "LiMn"
|
||||
"NiCd", "LiMn", "PbAc", "NiZn", "RAM", "ZnAr"
|
||||
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/voltage_avg,
|
||||
|
|
|
|||
|
|
@ -17,12 +17,16 @@ description: |
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,pm8916-pon
|
||||
- qcom,pm8941-pon
|
||||
- qcom,pms405-pon
|
||||
- qcom,pm8998-pon
|
||||
- qcom,pmk8350-pon
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,pm8916-pon
|
||||
- qcom,pm8941-pon
|
||||
- qcom,pms405-pon
|
||||
- qcom,pm8998-pon
|
||||
- qcom,pmk8350-pon
|
||||
- items:
|
||||
- const: qcom,pmm8654au-pon
|
||||
- const: qcom,pmk8350-pon
|
||||
|
||||
reg:
|
||||
description: |
|
||||
|
|
@ -79,6 +83,10 @@ allOf:
|
|||
reg-names:
|
||||
items:
|
||||
- const: pon
|
||||
patternProperties:
|
||||
# Negative look-ahead to disallow unsupported modes. The '$' has to be
|
||||
# part of lookahead group to work, instead of trailing outside of ().
|
||||
"^mode-(?!(bootloader$|recovery$))": false
|
||||
else:
|
||||
patternProperties:
|
||||
"^mode-.*$": false
|
||||
|
|
|
|||
|
|
@ -67,13 +67,11 @@ properties:
|
|||
Voltage threshold to report battery as over voltage (in mV).
|
||||
Default is not to report over-voltage events.
|
||||
|
||||
power-supplies: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
|||
|
|
@ -44,9 +44,40 @@ properties:
|
|||
maxItems: 1
|
||||
description: USB suspend pin (active high, output)
|
||||
|
||||
dc-current-limit-gpios:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description:
|
||||
GPIOs controlling DC input current limit via resistor mux.
|
||||
Used with dc-current-limit-mapping to select charging current.
|
||||
|
||||
dc-current-limit-mapping:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
minItems: 2
|
||||
maxItems: 16
|
||||
description: |
|
||||
Array of (current_microamps, gpio_bit_pattern) pairs defining available
|
||||
DC current limits. The gpio_bit_pattern is applied to dc-current-limit-gpios
|
||||
to select that current level.
|
||||
items:
|
||||
items:
|
||||
- description: Current limit in microamps
|
||||
- description: GPIO bit pattern value
|
||||
|
||||
usb-current-limit-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
GPIO driving the MAX8903 IUSB pin. IUSB is a silicon-fixed
|
||||
two-state input - low selects 100 mA, high selects 500 mA.
|
||||
Only the GPIO mapping is board-specific.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
dependentRequired:
|
||||
dc-current-limit-gpios: [dc-current-limit-mapping]
|
||||
dc-current-limit-mapping: [dc-current-limit-gpios]
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- dok-gpios
|
||||
|
|
@ -65,3 +96,25 @@ examples:
|
|||
chg-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
|
||||
cen-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
- |
|
||||
/* Example with DC and USB current limit control */
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
charger {
|
||||
compatible = "maxim,max8903";
|
||||
dok-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
|
||||
flt-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
|
||||
chg-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
|
||||
cen-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
|
||||
dcm-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
/* DC input current limit via IDC resistor mux */
|
||||
dc-current-limit-gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio1 1 GPIO_ACTIVE_HIGH>;
|
||||
dc-current-limit-mapping = <750000 0>, /* GPIO[1:0]=0b00 */
|
||||
<900000 1>, /* GPIO[1:0]=0b01 */
|
||||
<1400000 3>, /* GPIO[1:0]=0b11 */
|
||||
<2000000 2>; /* GPIO[1:0]=0b10 */
|
||||
|
||||
/* USB current limit: IUSB pin, 100 mA (low) / 500 mA (high) */
|
||||
usb-current-limit-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/sgmicro,sgm41542.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SGM41542 Battery Charger
|
||||
|
||||
description:
|
||||
The SGMicro SGM41542 is a single cell battery charger/boost controller.
|
||||
|
||||
maintainers:
|
||||
- Chris Morgan <macromorgan@hotmail.com>
|
||||
- Xu Shengfei <xsf@rock-chips.com>
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sgmicro,sgm41542
|
||||
|
||||
input-current-limit-microamp:
|
||||
default: 2400000
|
||||
minimum: 100000
|
||||
maximum: 3800000
|
||||
|
||||
input-voltage-limit-microvolt:
|
||||
default: 4500000
|
||||
minimum: 3900000
|
||||
maximum: 12000000
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
monitored-battery:
|
||||
description: |
|
||||
The charger uses the following battery properties
|
||||
constant-charge-current-max-microamp (default 2040000)
|
||||
constant-charge-voltage-max-microvolt (default 4208000)
|
||||
charge-term-current-microamp (default 180000)
|
||||
precharge-current-microamp (default 180000)
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
properties:
|
||||
otg-vbus:
|
||||
type: object
|
||||
description: OTG boost regulator
|
||||
$ref: /schemas/regulator/regulator.yaml
|
||||
unevaluatedProperties: false
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
battery: battery {
|
||||
compatible = "simple-battery";
|
||||
constant-charge-current-max-microamp = <10000000>;
|
||||
constant-charge-voltage-max-microvolt = <4350000>;
|
||||
precharge-current-microamp = <180000>;
|
||||
charge-term-current-microamp = <300000>;
|
||||
};
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
charger@3b {
|
||||
compatible = "sgmicro,sgm41542";
|
||||
reg = <0x3b>;
|
||||
input-current-limit-microamp = <3000000>;
|
||||
input-voltage-limit-microvolt = <4500000>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
|
||||
monitored-battery = <&battery>;
|
||||
|
||||
regulators {
|
||||
otg-vbus {
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/ti,bq25630.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI BQ25630 battery charger
|
||||
|
||||
maintainers:
|
||||
- Waqar Hameed <waqar.hameed@axis.com>
|
||||
|
||||
description: |
|
||||
I2C controlled single cell Li-ion and Li-polymer 5A buck charger.
|
||||
Datasheet: https://www.ti.com/lit/gpn/bq25630
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,bq25630
|
||||
|
||||
reg:
|
||||
const: 0x6b
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
Device sends active low 256 µs pulse. Type should therefore be
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- monitored-battery
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
charger@6b {
|
||||
compatible = "ti,bq25630";
|
||||
reg = <0x6b>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
monitored-battery = <&bat>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -10312,6 +10312,12 @@ S: Maintained
|
|||
F: drivers/mmc/host/sdhci-esdhc-mcf.c
|
||||
F: include/linux/platform_data/mmc-esdhc-mcf.h
|
||||
|
||||
FREESCALE COLDFIRE M5441X RCM POWER-ON REASON DRIVER
|
||||
M: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
|
||||
L: linux-m68k@lists.linux-m68k.org
|
||||
S: Maintained
|
||||
F: drivers/power/reset/mcf-rcm-reset.c
|
||||
|
||||
FREESCALE DIU FRAMEBUFFER DRIVER
|
||||
M: Timur Tabi <timur@kernel.org>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -128,6 +128,18 @@ config POWER_RESET_LINKSTATION
|
|||
|
||||
Say Y here if you have a Buffalo LinkStation LS421D/E.
|
||||
|
||||
config POWER_RESET_MCF_RCM
|
||||
tristate "Freescale ColdFire RCM power-on reason driver"
|
||||
depends on M5441x || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This driver exposes the cause of the last reset on Freescale
|
||||
ColdFire 5441x SoCs through the standard power_on_reason sysfs
|
||||
ABI. It reads the Reset Status Register (RSR) of the Reset
|
||||
Controller Module once at probe time and reports a normalised
|
||||
string (regular power-up, reset button action, software reset
|
||||
or unknown reason).
|
||||
|
||||
config POWER_RESET_MACSMC
|
||||
tristate "Apple SMC reset/power-off driver"
|
||||
depends on MFD_MACSMC
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
|||
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_MCF_RCM) += mcf-rcm-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
|
||||
|
|
|
|||
126
drivers/power/reset/mcf-rcm-reset.c
Normal file
126
drivers/power/reset/mcf-rcm-reset.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Freescale ColdFire MCF5441x RCM power-on reason driver
|
||||
*
|
||||
* Copyright (C) 2026 Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power/power_on_reason.h>
|
||||
|
||||
/*
|
||||
* Reset Status Register (RSR) layout, MCF54418 Reference Manual chapter
|
||||
* 12.3.2. The register is 8-bit read-only and latches the cause (or
|
||||
* causes) of the most recent reset until the next one occurs.
|
||||
*/
|
||||
#define MCF_RSR_SOFT 0x20 /* Last reset caused by software */
|
||||
#define MCF_RSR_LOC 0x10 /* Last reset caused by PLL loss of clock */
|
||||
#define MCF_RSR_POR 0x08 /* Last reset caused by power-on */
|
||||
#define MCF_RSR_EXT 0x04 /* Last reset caused by external pin */
|
||||
#define MCF_RSR_WDRCORE 0x02 /* Last reset caused by core watchdog */
|
||||
#define MCF_RSR_LOL 0x01 /* Last reset caused by PLL loss of lock */
|
||||
|
||||
#define MCF_RSR_KNOWN_CAUSES (MCF_RSR_POR | MCF_RSR_EXT | MCF_RSR_WDRCORE | \
|
||||
MCF_RSR_LOC | MCF_RSR_LOL | MCF_RSR_SOFT)
|
||||
|
||||
struct mcf_rcm {
|
||||
const char *reason;
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode RSR into a power_on_reason string.
|
||||
*
|
||||
* The MCF5441x Reset Status Register can latch several cause bits at the
|
||||
* same time (Reference Manual chapter 12.3.2: "one or more status bits
|
||||
* may be set at the same time"). A power-on, for example, also resets
|
||||
* the PLL and may co-flag LOC and LOL during the boot sequence. The
|
||||
* power_on_reason ABI carries a single string, so this routine picks
|
||||
* one cause; the chosen priority surfaces the most explanatory one for
|
||||
* diagnostics:
|
||||
*
|
||||
* POR cold boot dominates any spurious co-flagged cause
|
||||
* EXT operator action via the RESET pin
|
||||
* WDRCORE core watchdog timeout, a fault to investigate
|
||||
* LOC, LOL PLL clock or lock failure, hardware fault
|
||||
* SOFT explicit software-requested reset
|
||||
*/
|
||||
static const char *mcf_rcm_decode(u8 rsr)
|
||||
{
|
||||
if (rsr & MCF_RSR_POR)
|
||||
return POWER_ON_REASON_REGULAR;
|
||||
if (rsr & MCF_RSR_EXT)
|
||||
return POWER_ON_REASON_RST_BTN;
|
||||
if (rsr & MCF_RSR_WDRCORE)
|
||||
return POWER_ON_REASON_WATCHDOG;
|
||||
if (rsr & (MCF_RSR_LOC | MCF_RSR_LOL))
|
||||
return POWER_ON_REASON_CPU_CLK_FAIL;
|
||||
if (rsr & MCF_RSR_SOFT)
|
||||
return POWER_ON_REASON_SOFTWARE;
|
||||
return POWER_ON_REASON_UNKNOWN;
|
||||
}
|
||||
|
||||
static ssize_t power_on_reason_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcf_rcm *rcm = platform_get_drvdata(to_platform_device(dev));
|
||||
|
||||
return sysfs_emit(buf, "%s\n", rcm->reason);
|
||||
}
|
||||
static DEVICE_ATTR_RO(power_on_reason);
|
||||
|
||||
static struct attribute *mcf_rcm_attrs[] = {
|
||||
&dev_attr_power_on_reason.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(mcf_rcm);
|
||||
|
||||
static int mcf_rcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mcf_rcm *rcm;
|
||||
void __iomem *base;
|
||||
u8 rsr;
|
||||
|
||||
rcm = devm_kzalloc(&pdev->dev, sizeof(*rcm), GFP_KERNEL);
|
||||
if (!rcm)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
rsr = readb_relaxed(base);
|
||||
rcm->reason = mcf_rcm_decode(rsr);
|
||||
|
||||
platform_set_drvdata(pdev, rcm);
|
||||
|
||||
if (!(rsr & MCF_RSR_KNOWN_CAUSES))
|
||||
dev_warn(&pdev->dev, "Unknown reset cause (RSR=0x%02x)\n", rsr);
|
||||
else
|
||||
dev_info(&pdev->dev, "Starting after %s (RSR=0x%02x)\n",
|
||||
rcm->reason, rsr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id mcf_rcm_id[] = {
|
||||
{ "mcf-rcm-reset" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mcf_rcm_id);
|
||||
|
||||
static struct platform_driver mcf_rcm_driver = {
|
||||
.probe = mcf_rcm_probe,
|
||||
.id_table = mcf_rcm_id,
|
||||
.driver = {
|
||||
.name = "mcf-rcm-reset",
|
||||
.dev_groups = mcf_rcm_groups,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mcf_rcm_driver);
|
||||
|
||||
MODULE_AUTHOR("Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>");
|
||||
MODULE_DESCRIPTION("Freescale ColdFire RCM power-on reason driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -96,6 +96,7 @@ static const struct pci_device_id piix4_poweroff_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
|
||||
{ 0 },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, piix4_poweroff_ids);
|
||||
|
||||
static struct pci_driver piix4_poweroff_driver = {
|
||||
.name = "piix4-poweroff",
|
||||
|
|
|
|||
|
|
@ -68,8 +68,6 @@ static int pwr_mlxbf_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
|
||||
err = devm_request_irq(dev, irq, pwr_mlxbf_irq, 0, hid, priv);
|
||||
if (err)
|
||||
dev_err(dev, "Failed request of %s irq\n", priv->hid);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/reboot-mode.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -168,10 +169,11 @@ static int reboot_mode_create_device(struct reboot_mode_driver *reboot)
|
|||
*/
|
||||
int reboot_mode_register(struct reboot_mode_driver *reboot)
|
||||
{
|
||||
struct mode_info *info;
|
||||
struct mode_info *info = NULL;
|
||||
struct property *prop;
|
||||
struct device_node *np = reboot->dev->of_node;
|
||||
size_t len = strlen(PREFIX);
|
||||
u32 magic;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&reboot->head);
|
||||
|
|
@ -180,22 +182,22 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
|
|||
if (strncmp(prop->name, PREFIX, len))
|
||||
continue;
|
||||
|
||||
info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (device_property_read_u32(reboot->dev, prop->name, &magic)) {
|
||||
dev_dbg(reboot->dev, "reboot mode %s without magic number\n",
|
||||
prop->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
info = kzalloc_obj(*info, GFP_KERNEL);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, prop->name, &info->magic)) {
|
||||
dev_err(reboot->dev, "reboot mode %s without magic number\n",
|
||||
info->mode);
|
||||
devm_kfree(reboot->dev, info);
|
||||
continue;
|
||||
}
|
||||
|
||||
info->magic = magic;
|
||||
info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
|
||||
if (!info->mode) {
|
||||
ret = -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
} else if (info->mode[0] == '\0') {
|
||||
kfree_const(info->mode);
|
||||
|
|
@ -206,6 +208,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
|
|||
}
|
||||
|
||||
list_add_tail(&info->list, &reboot->head);
|
||||
info = NULL;
|
||||
}
|
||||
|
||||
reboot->reboot_notifier.notifier_call = reboot_mode_notify;
|
||||
|
|
@ -218,6 +221,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
|
|||
return 0;
|
||||
|
||||
error:
|
||||
kfree(info);
|
||||
reboot_mode_unregister(reboot);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -261,12 +265,16 @@ static inline void reboot_mode_unregister_device(struct reboot_mode_driver *rebo
|
|||
int reboot_mode_unregister(struct reboot_mode_driver *reboot)
|
||||
{
|
||||
struct mode_info *info;
|
||||
struct mode_info *next;
|
||||
|
||||
unregister_reboot_notifier(&reboot->reboot_notifier);
|
||||
reboot_mode_unregister_device(reboot);
|
||||
|
||||
list_for_each_entry(info, &reboot->head, list)
|
||||
list_for_each_entry_safe(info, next, &reboot->head, list) {
|
||||
list_del(&info->list);
|
||||
kfree_const(info->mode);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct syscon_reboot_context *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
int priority;
|
||||
u32 priority;
|
||||
int err;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
|
|
@ -81,7 +81,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(ctx->map);
|
||||
}
|
||||
|
||||
if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
|
||||
if (of_property_read_u32(pdev->dev.of_node, "priority", &priority))
|
||||
priority = 192;
|
||||
|
||||
ctx->rd = of_device_get_match_data(dev);
|
||||
|
|
|
|||
|
|
@ -959,20 +959,14 @@ static int pm860x_battery_probe(struct platform_device *pdev)
|
|||
ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
|
||||
pm860x_coulomb_handler, IRQF_ONESHOT,
|
||||
"coulomb", info);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
info->irq_cc, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
|
||||
pm860x_batt_handler,
|
||||
IRQF_ONESHOT, "battery", info);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
info->irq_batt, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -716,11 +716,8 @@ static int pm860x_charger_probe(struct platform_device *pdev)
|
|||
pm860x_irq_descs[i].handler,
|
||||
IRQF_ONESHOT,
|
||||
pm860x_irq_descs[i].name, info);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
info->irq[i], ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -839,6 +839,13 @@ config CHARGER_BQ25980
|
|||
Say Y to enable support for the TI BQ25980, BQ25975 and BQ25960
|
||||
series of fast battery chargers.
|
||||
|
||||
config CHARGER_BQ25630
|
||||
tristate "TI BQ25630 battery charger driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to enable support for the TI BQ25630 battery charger.
|
||||
|
||||
config CHARGER_BQ256XX
|
||||
tristate "TI BQ256XX battery charger driver"
|
||||
depends on I2C
|
||||
|
|
@ -866,6 +873,15 @@ config CHARGER_S2M
|
|||
devices provide USB power supply information and also required
|
||||
for USB OTG role switching.
|
||||
|
||||
config CHARGER_SGM41542
|
||||
tristate "SGM41542 charger driver"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on REGULATOR
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to enable support for the SGM41542 battery charger.
|
||||
|
||||
config CHARGER_SMB347
|
||||
tristate "Summit Microelectronics SMB3XX Battery Charger"
|
||||
depends on I2C
|
||||
|
|
|
|||
|
|
@ -105,9 +105,11 @@ obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o
|
|||
obj-$(CONFIG_CHARGER_BQ257XX) += bq257xx_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ25630) += bq25630_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
|
||||
obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o
|
||||
obj-$(CONFIG_CHARGER_S2M) += s2m-charger.o
|
||||
obj-$(CONFIG_CHARGER_SGM41542) += sgm41542_charger.o
|
||||
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
|
||||
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
|
||||
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
|
||||
|
|
|
|||
|
|
@ -787,11 +787,8 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
|||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_btemp_irq[i].name, di);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n"
|
||||
, ab8500_btemp_irq[i].name, irq, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_btemp_irq[i].name, irq, ret);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3605,11 +3605,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_charger_irq[i].name, di);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n"
|
||||
, ab8500_charger_irq[i].name, irq, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_charger_irq[i].name, irq, ret);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3054,6 +3054,20 @@ static void ab8500_fg_unbind(struct device *dev, struct device *master,
|
|||
flush_workqueue(di->fg_wq);
|
||||
}
|
||||
|
||||
/* Disable, not cancel: works stay disabled so nothing can re-arm them. */
|
||||
static void ab8500_fg_destroy_workqueue(void *data)
|
||||
{
|
||||
struct ab8500_fg *di = data;
|
||||
|
||||
disable_work_sync(&di->fg_acc_cur_work);
|
||||
disable_work_sync(&di->fg_work);
|
||||
disable_delayed_work_sync(&di->fg_reinit_work);
|
||||
disable_delayed_work_sync(&di->fg_low_bat_work);
|
||||
disable_delayed_work_sync(&di->fg_check_hw_failure_work);
|
||||
disable_delayed_work_sync(&di->fg_periodic_work);
|
||||
destroy_workqueue(di->fg_wq);
|
||||
}
|
||||
|
||||
static const struct component_ops ab8500_fg_component_ops = {
|
||||
.bind = ab8500_fg_bind,
|
||||
.unbind = ab8500_fg_unbind,
|
||||
|
|
@ -3155,6 +3169,11 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(di->fg_psy);
|
||||
}
|
||||
|
||||
/* Registered after fg_psy, before the IRQs: devm frees IRQ -> workqueue -> fg_psy. */
|
||||
ret = devm_add_action_or_reset(dev, ab8500_fg_destroy_workqueue, di);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
|
||||
|
||||
/*
|
||||
|
|
@ -3167,22 +3186,16 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
/* Register primary interrupt handlers */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
|
||||
if (irq < 0) {
|
||||
destroy_workqueue(di->fg_wq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
ab8500_fg_irq[i].isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_fg_irq[i].name, di);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n",
|
||||
ab8500_fg_irq[i].name, irq, ret);
|
||||
destroy_workqueue(di->fg_wq);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_fg_irq[i].name, irq, ret);
|
||||
}
|
||||
|
|
@ -3196,7 +3209,6 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
ret = ab8500_fg_sysfs_init(di);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create sysfs entry\n");
|
||||
destroy_workqueue(di->fg_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -3204,7 +3216,6 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(dev, "failed to create FG psy\n");
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
destroy_workqueue(di->fg_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -3224,7 +3235,6 @@ static void ab8500_fg_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ab8500_fg *di = platform_get_drvdata(pdev);
|
||||
|
||||
destroy_workqueue(di->fg_wq);
|
||||
component_del(&pdev->dev, &ab8500_fg_component_ops);
|
||||
list_del(&di->node);
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
|
|
|
|||
|
|
@ -620,10 +620,8 @@ static int act8945a_charger_probe(struct platform_device *pdev)
|
|||
ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,
|
||||
IRQF_TRIGGER_FALLING, "act8945a_interrupt",
|
||||
charger);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request nIRQ pin IRQ\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, charger);
|
||||
|
||||
|
|
|
|||
|
|
@ -383,11 +383,8 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
|||
ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
|
||||
axp20x_ac_power_irq, 0,
|
||||
DRVNAME, power);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
|
||||
axp_data->irq_names[i], ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1030,11 +1030,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
|||
ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
|
||||
axp20x_usb_power_irq, 0,
|
||||
DRVNAME, power);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
|
||||
axp_data->irq_names[i], ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (axp20x_usb_vbus_needs_polling(power))
|
||||
|
|
|
|||
|
|
@ -946,8 +946,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
NULL, axp288_charger_irq_thread_handler,
|
||||
IRQF_ONESHOT, info->pdev->name, info);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request interrupt=%d\n",
|
||||
info->irq[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -791,7 +791,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
fuel_gauge_thread_handler,
|
||||
IRQF_ONESHOT, DEV_NAME, info);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "requesting IRQ %d\n", info->irq[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -191,12 +191,13 @@ static int bd71828_get_current_ds_adc(struct bd71828_power *pwr, int *curr, int
|
|||
{
|
||||
__be16 tmp_curr;
|
||||
char *tmp = (char *)&tmp_curr;
|
||||
int dir = 1;
|
||||
int regs[] = { pwr->regs->ibat, pwr->regs->ibat_avg };
|
||||
int *vals[] = { curr, curr_avg };
|
||||
int ret, i;
|
||||
|
||||
for (dir = 1, i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
int dir = 1;
|
||||
|
||||
ret = regmap_bulk_read(pwr->regmap, regs[i], &tmp_curr,
|
||||
sizeof(tmp_curr));
|
||||
if (ret)
|
||||
|
|
@ -224,13 +225,13 @@ static int bd71815_get_temp(struct bd71828_power *pwr, int *temp)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
t = 200 - t;
|
||||
|
||||
if (t > 200) {
|
||||
dev_err(pwr->dev, "Failed to read battery temperature\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
*temp = 200 - t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -454,8 +455,10 @@ static int bd71828_charger_get_property(struct power_supply *psy,
|
|||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = get_chg_online(pwr, &online);
|
||||
if (!ret)
|
||||
val->intval = online;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = online;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
|
||||
|
|
@ -638,7 +641,6 @@ static const enum power_supply_property bd71828_charger_props[] = {
|
|||
|
||||
static const enum power_supply_property bd71828_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
|
|
@ -1217,7 +1219,9 @@ static int bd71828_power_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(&pdev->dev, ret, "sense resistor missing\n");
|
||||
|
||||
dev_set_drvdata(&pdev->dev, pwr);
|
||||
bd71828_init_hardware(pwr);
|
||||
ret = bd71828_init_hardware(pwr);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "hardware init failed\n");
|
||||
|
||||
bat_cfg.drv_data = pwr;
|
||||
bat_cfg.fwnode = dev_fwnode(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -279,17 +279,7 @@ enum bd9995x_fields {
|
|||
F_VBUS_EXTID,
|
||||
F_VBUS_IDRDET,
|
||||
F_VBUS_INDO,
|
||||
F_VBUS_UCDSWEN,
|
||||
F_VBUS_RREF_EN,
|
||||
F_VBUS_DPPU_EN,
|
||||
F_VBUS_DPREF_EN,
|
||||
F_VBUS_DMREF_EN,
|
||||
F_VBUS_DPDET_EN,
|
||||
F_VBUS_DMDET_EN,
|
||||
F_VBUS_DPSINK_EN,
|
||||
F_VBUS_DMSINK_EN,
|
||||
F_VBUS_DP_BUFF_EN,
|
||||
F_VBUS_DM_BUFF_EN,
|
||||
|
||||
F_VBUS_EXTCLKENBL,
|
||||
F_VBUS_PLSTESTEN,
|
||||
F_VBUS_UCDSWEN_TSTENB,
|
||||
|
|
@ -626,17 +616,6 @@ static const struct reg_field bd9995x_reg_fields[] = {
|
|||
[F_VBUS_EXTID] = REG_FIELD(VBUS_IDD_STATUS, 5, 5),
|
||||
[F_VBUS_IDRDET] = REG_FIELD(VBUS_IDD_STATUS, 4, 4),
|
||||
[F_VBUS_INDO] = REG_FIELD(VBUS_IDD_STATUS, 0, 3),
|
||||
[F_VBUS_UCDSWEN] = REG_FIELD(VCC_UCD_FCTRL_SET, 10, 10),
|
||||
[F_VBUS_RREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 9, 9),
|
||||
[F_VBUS_DPPU_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 8, 8),
|
||||
[F_VBUS_DPREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 7, 7),
|
||||
[F_VBUS_DMREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 6, 6),
|
||||
[F_VBUS_DPDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 5, 5),
|
||||
[F_VBUS_DMDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 4, 4),
|
||||
[F_VBUS_DPSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 3, 3),
|
||||
[F_VBUS_DMSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 2, 2),
|
||||
[F_VBUS_DP_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 1, 1),
|
||||
[F_VBUS_DM_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 0, 0),
|
||||
|
||||
[F_VBUS_EXTCLKENBL] = REG_FIELD(VBUS_UCD_FCTRL_EN, 15, 15),
|
||||
[F_VBUS_PLSTESTEN] = REG_FIELD(VBUS_UCD_FCTRL_EN, 14, 14),
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ struct bq24190_dev_info {
|
|||
struct mutex f_reg_lock;
|
||||
u8 f_reg;
|
||||
u8 ss_reg;
|
||||
u8 watchdog;
|
||||
const struct bq24190_chip_info *info;
|
||||
};
|
||||
|
||||
|
|
@ -751,13 +750,6 @@ static int bq24190_set_config(struct bq24190_dev_info *bdi)
|
|||
int ret;
|
||||
u8 v;
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >>
|
||||
BQ24190_REG_CTTC_WATCHDOG_SHIFT);
|
||||
|
||||
/*
|
||||
* According to the "Host Mode and default Mode" section of the
|
||||
* manual, a write to any register causes the bq24190 to switch
|
||||
|
|
@ -766,9 +758,9 @@ static int bq24190_set_config(struct bq24190_dev_info *bdi)
|
|||
* So, by simply turning off the WDT, we accomplish both with the
|
||||
* same write.
|
||||
*/
|
||||
v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK;
|
||||
|
||||
ret = bq24190_write(bdi, BQ24190_REG_CTTC, v);
|
||||
ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC,
|
||||
BQ24190_REG_CTTC_WATCHDOG_MASK,
|
||||
BQ24190_REG_CTTC_WATCHDOG_SHIFT, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -2169,10 +2161,8 @@ static int bq24190_probe(struct i2c_client *client)
|
|||
bq24190_irq_handler_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"bq24190-charger", bdi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Can't set up irq handler\n");
|
||||
if (ret < 0)
|
||||
goto out_charger;
|
||||
}
|
||||
|
||||
ret = bq24190_register_vbus_regulator(bdi);
|
||||
if (ret < 0)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -1003,10 +1004,6 @@ static int bq24257_probe(struct i2c_client *client)
|
|||
if (bq->info->chip == BQ24250)
|
||||
bq->iilimit_autoset_enable = false;
|
||||
|
||||
if (bq->iilimit_autoset_enable)
|
||||
INIT_DELAYED_WORK(&bq->iilimit_setup_work,
|
||||
bq24257_iilimit_setup_work);
|
||||
|
||||
/*
|
||||
* The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's
|
||||
* not probe for it and instead use a SW-based approach to determine
|
||||
|
|
@ -1047,15 +1044,21 @@ static int bq24257_probe(struct i2c_client *client)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (bq->iilimit_autoset_enable) {
|
||||
ret = devm_delayed_work_autocancel(dev,
|
||||
&bq->iilimit_setup_work,
|
||||
bq24257_iilimit_setup_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
bq24257_irq_handler_thread,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
bq->info->name, bq);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request IRQ #%d\n", client->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1064,9 +1067,6 @@ static void bq24257_remove(struct i2c_client *client)
|
|||
{
|
||||
struct bq24257_device *bq = i2c_get_clientdata(client);
|
||||
|
||||
if (bq->iilimit_autoset_enable)
|
||||
cancel_delayed_work_sync(&bq->iilimit_setup_work);
|
||||
|
||||
bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -462,12 +462,8 @@ static int bq24735_charger_probe(struct i2c_client *client)
|
|||
IRQF_ONESHOT,
|
||||
supply_desc->name,
|
||||
charger->charger);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to register IRQ %d err %d\n",
|
||||
client->irq, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = device_property_read_u32(&client->dev, "poll-interval",
|
||||
&charger->poll_interval);
|
||||
|
|
|
|||
1075
drivers/power/supply/bq25630_charger.c
Normal file
1075
drivers/power/supply/bq25630_charger.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -347,12 +347,8 @@ static int bq256xx_array_parse(int array_size, int val, const int array[])
|
|||
if (val == array[i])
|
||||
return i;
|
||||
|
||||
if (val > array[i - 1] && val < array[i]) {
|
||||
if (val < array[i])
|
||||
return i - 1;
|
||||
else
|
||||
return i;
|
||||
}
|
||||
if (val > array[i - 1] && val < array[i])
|
||||
return i - 1;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -896,6 +892,8 @@ static void bq256xx_charger_reset(void *data)
|
|||
|
||||
if (!IS_ERR_OR_NULL(bq->usb3_phy))
|
||||
usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb);
|
||||
|
||||
cancel_work_sync(&bq->usb_work);
|
||||
}
|
||||
|
||||
static int bq256xx_set_charger_property(struct power_supply *psy,
|
||||
|
|
@ -1721,24 +1719,12 @@ static int bq256xx_probe(struct i2c_client *client)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
|
||||
if (ret)
|
||||
return ret;
|
||||
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
|
||||
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
|
||||
|
||||
/* OTG reporting */
|
||||
bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (!IS_ERR_OR_NULL(bq->usb2_phy)) {
|
||||
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
|
||||
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
|
||||
usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
|
||||
}
|
||||
|
||||
bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
if (!IS_ERR_OR_NULL(bq->usb3_phy)) {
|
||||
INIT_WORK(&bq->usb_work, bq256xx_usb_work);
|
||||
bq->usb_nb.notifier_call = bq256xx_usb_notifier;
|
||||
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
|
||||
}
|
||||
|
||||
ret = bq256xx_power_supply_init(bq, &psy_cfg, dev);
|
||||
if (ret) {
|
||||
|
|
@ -1746,16 +1732,25 @@ static int bq256xx_probe(struct i2c_client *client)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register after the power supplies so devm runs it first. */
|
||||
ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!IS_ERR_OR_NULL(bq->usb2_phy))
|
||||
usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
|
||||
|
||||
if (!IS_ERR_OR_NULL(bq->usb3_phy))
|
||||
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
bq256xx_irq_handler_thread,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&client->dev), bq);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "get irq fail: %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bq256xx_hw_init(bq);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/bq257xx.h>
|
||||
|
|
@ -88,6 +89,53 @@ struct bq257xx_chg {
|
|||
u32 vsys_min;
|
||||
};
|
||||
|
||||
/**
|
||||
* bq25792_read16() - Read a 16-bit value from device register
|
||||
* @pdata: driver platform data
|
||||
* @reg: register address to read from
|
||||
* @val: pointer to store the register value
|
||||
*
|
||||
* Read a 16-bit big-endian value from the BQ25792 device via regmap
|
||||
* and convert to CPU byte order.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read.
|
||||
*/
|
||||
static int bq25792_read16(struct bq257xx_chg *pdata, unsigned int reg, u16 *val)
|
||||
{
|
||||
__be16 regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_raw_read(pdata->bq->regmap, reg, ®val, sizeof(regval));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(regval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_write16() - Write a 16-bit value to device register
|
||||
* @pdata: driver platform data
|
||||
* @reg: register address to write to
|
||||
* @val: 16-bit value to write in CPU byte order
|
||||
*
|
||||
* Convert the value to big-endian and write a 16-bit value to the
|
||||
* BQ25792 device via regmap.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to write.
|
||||
*/
|
||||
static int bq25792_write16(struct bq257xx_chg *pdata, unsigned int reg, u16 val)
|
||||
{
|
||||
__be16 regval = cpu_to_be16(val);
|
||||
int ret;
|
||||
|
||||
ret = regmap_raw_write(pdata->bq->regmap, reg, ®val, sizeof(regval));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_state() - Get the current state of the device
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -119,6 +167,43 @@ static int bq25703_get_state(struct bq257xx_chg *pdata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_state() - Get the current state of the device
|
||||
* @pdata: driver platform data
|
||||
*
|
||||
* Get the current state of the BQ25792 charger by reading status
|
||||
* registers. Updates the online, charging, overvoltage, and fault
|
||||
* status fields in the driver data structure.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read device.
|
||||
*/
|
||||
static int bq25792_get_state(struct bq257xx_chg *pdata)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pdata->bq->regmap, BQ25792_REG1B_CHARGER_STATUS_0, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdata->online = reg & BQ25792_REG1B_PG_STAT;
|
||||
|
||||
ret = regmap_read(pdata->bq->regmap, BQ25792_REG1C_CHARGER_STATUS_1, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdata->charging = reg & BQ25792_REG1C_CHG_STAT_MASK;
|
||||
|
||||
ret = regmap_read(pdata->bq->regmap, BQ25792_REG20_FAULT_STATUS_0, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdata->overvoltage = reg & BQ25792_REG20_OVERVOLTAGE_MASK;
|
||||
pdata->oc_fault = reg & BQ25792_REG20_OVERCURRENT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_min_vsys() - Get the minimum system voltage
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -142,6 +227,31 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_min_vsys() - Get the minimum system voltage
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the minimum voltage value
|
||||
*
|
||||
* Read the current minimum system voltage setting from the device
|
||||
* and return it in microvolts.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read.
|
||||
*/
|
||||
static int bq25792_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pdata->bq->regmap, BQ25792_REG00_MIN_SYS_VOLTAGE, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = FIELD_GET(BQ25792_REG00_VSYSMIN_MASK, reg);
|
||||
*intval = (reg * BQ25792_MINVSYS_STEP_UV) + BQ25792_MINVSYS_MIN_UV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_set_min_vsys() - Set the minimum system voltage
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -166,6 +276,29 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
|
|||
reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_set_min_vsys() - Set the minimum system voltage
|
||||
* @pdata: driver platform data
|
||||
* @vsys: voltage value to set in uV
|
||||
*
|
||||
* Set the minimum system voltage by clamping the requested value
|
||||
* between device limits and writing to the appropriate register.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to write.
|
||||
*/
|
||||
static int bq25792_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
|
||||
{
|
||||
unsigned int reg;
|
||||
int vsys_min = pdata->vsys_min;
|
||||
|
||||
vsys = clamp(vsys, vsys_min, BQ25792_MINVSYS_MAX_UV);
|
||||
reg = ((vsys - BQ25792_MINVSYS_MIN_UV) / BQ25792_MINVSYS_STEP_UV);
|
||||
reg = FIELD_PREP(BQ25792_REG00_VSYSMIN_MASK, reg);
|
||||
|
||||
return regmap_write(pdata->bq->regmap,
|
||||
BQ25792_REG00_MIN_SYS_VOLTAGE, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_cur() - Get the reported current from the battery
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -195,6 +328,30 @@ static int bq25703_get_cur(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_cur() - Get the reported current from the battery
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the battery current value
|
||||
*
|
||||
* Read the current ADC value from the device representing the battery
|
||||
* charge or discharge current and return it in microamps.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read.
|
||||
*/
|
||||
static int bq25792_get_cur(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
ret = bq25792_read16(pdata, BQ25792_REG33_IBAT_ADC, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*intval = (s16)reg * BQ25792_ADCIBAT_STEP_UA;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_ichg_cur() - Get the maximum reported charge current
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -218,6 +375,30 @@ static int bq25703_get_ichg_cur(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_ichg_cur() - Get the maximum reported charge current
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the maximum charge current value
|
||||
*
|
||||
* Read the programmed maximum charge current limit from the device.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read value.
|
||||
*/
|
||||
static int bq25792_get_ichg_cur(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
ret = bq25792_read16(pdata, BQ25792_REG03_CHARGE_CURRENT_LIMIT, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*intval = FIELD_GET(BQ25792_REG03_ICHG_MASK, reg) *
|
||||
BQ25792_ICHG_STEP_UA;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_set_ichg_cur() - Set the maximum charge current
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -242,6 +423,28 @@ static int bq25703_set_ichg_cur(struct bq257xx_chg *pdata, int ichg)
|
|||
reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_set_ichg_cur() - Set the maximum charge current
|
||||
* @pdata: driver platform data
|
||||
* @ichg: current value to set in uA
|
||||
*
|
||||
* Set the maximum charge current by clamping the requested value
|
||||
* between device limits and writing to the appropriate register.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to write.
|
||||
*/
|
||||
static int bq25792_set_ichg_cur(struct bq257xx_chg *pdata, int ichg)
|
||||
{
|
||||
int ichg_max = pdata->ichg_max;
|
||||
u16 reg;
|
||||
|
||||
ichg = clamp(ichg, BQ25792_ICHG_MIN_UA, ichg_max);
|
||||
reg = FIELD_PREP(BQ25792_REG03_ICHG_MASK,
|
||||
(ichg / BQ25792_ICHG_STEP_UA));
|
||||
|
||||
return bq25792_write16(pdata, BQ25792_REG03_CHARGE_CURRENT_LIMIT, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_chrg_volt() - Get the maximum set charge voltage
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -265,6 +468,30 @@ static int bq25703_get_chrg_volt(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_chrg_volt() - Get the maximum set charge voltage
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the maximum charge voltage value
|
||||
*
|
||||
* Read the current charge voltage limit from the device.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read value.
|
||||
*/
|
||||
static int bq25792_get_chrg_volt(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
ret = bq25792_read16(pdata, BQ25792_REG01_CHARGE_VOLTAGE_LIMIT, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*intval = FIELD_GET(BQ25792_REG01_VREG_MASK, reg) *
|
||||
BQ25792_VBATREG_STEP_UV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_set_chrg_volt() - Set the maximum charge voltage
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -291,6 +518,29 @@ static int bq25703_set_chrg_volt(struct bq257xx_chg *pdata, int vbat)
|
|||
reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_set_chrg_volt() - Set the maximum charge voltage
|
||||
* @pdata: driver platform data
|
||||
* @vbat: voltage value to set in uV
|
||||
*
|
||||
* Set the maximum charge voltage by clamping the requested value
|
||||
* between device limits and writing to the appropriate register.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to write.
|
||||
*/
|
||||
static int bq25792_set_chrg_volt(struct bq257xx_chg *pdata, int vbat)
|
||||
{
|
||||
int vbat_max = pdata->vbat_max;
|
||||
u16 reg;
|
||||
|
||||
vbat = clamp(vbat, BQ25792_VBATREG_MIN_UV, vbat_max);
|
||||
|
||||
reg = FIELD_PREP(BQ25792_REG01_VREG_MASK,
|
||||
(vbat / BQ25792_VBATREG_STEP_UV));
|
||||
|
||||
return bq25792_write16(pdata, BQ25792_REG01_CHARGE_VOLTAGE_LIMIT, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_iindpm() - Get the maximum set input current
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -319,6 +569,30 @@ static int bq25703_get_iindpm(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_iindpm() - Get the maximum set input current
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the maximum input current value
|
||||
*
|
||||
* Read the current input current limit from the device.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read value.
|
||||
*/
|
||||
static int bq25792_get_iindpm(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
ret = bq25792_read16(pdata, BQ25792_REG06_INPUT_CURRENT_LIMIT, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = FIELD_GET(BQ25792_REG06_IINDPM_MASK, reg);
|
||||
*intval = reg * BQ25792_IINDPM_STEP_UA;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_set_iindpm() - Set the maximum input current
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -344,6 +618,29 @@ static int bq25703_set_iindpm(struct bq257xx_chg *pdata, int iindpm)
|
|||
FIELD_PREP(BQ25703_IINDPM_MASK, reg));
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_set_iindpm() - Set the maximum input current
|
||||
* @pdata: driver platform data
|
||||
* @iindpm: current value in uA
|
||||
*
|
||||
* Set the maximum input current by clamping the requested value
|
||||
* between device limits and writing to the appropriate register.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to write.
|
||||
*/
|
||||
static int bq25792_set_iindpm(struct bq257xx_chg *pdata, int iindpm)
|
||||
{
|
||||
u16 reg;
|
||||
int iindpm_max = pdata->iindpm_max;
|
||||
|
||||
iindpm = clamp(iindpm, BQ25792_IINDPM_MIN_UA, iindpm_max);
|
||||
|
||||
reg = iindpm / BQ25792_IINDPM_STEP_UA;
|
||||
|
||||
return bq25792_write16(pdata, BQ25792_REG06_INPUT_CURRENT_LIMIT,
|
||||
FIELD_PREP(BQ25792_REG06_IINDPM_MASK, reg));
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_get_vbat() - Get the reported voltage from the battery
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -368,6 +665,30 @@ static int bq25703_get_vbat(struct bq257xx_chg *pdata, int *intval)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_get_vbat() - Get the reported voltage from the battery
|
||||
* @pdata: driver platform data
|
||||
* @intval: pointer to store the battery voltage value
|
||||
*
|
||||
* Read the current ADC value representing the battery voltage
|
||||
* and return it in microvolts.
|
||||
*
|
||||
* Return: Returns 0 on success or error on failure to read value.
|
||||
*/
|
||||
static int bq25792_get_vbat(struct bq257xx_chg *pdata, int *intval)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
ret = bq25792_read16(pdata, BQ25792_REG3B_VBAT_ADC, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*intval = reg * BQ25792_ADCVSYSVBAT_STEP_UV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_hw_init() - Set all the required registers to init the charger
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -434,6 +755,108 @@ static int bq25703_hw_init(struct bq257xx_chg *pdata)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_hw_init() - Initialize BQ25792 hardware
|
||||
* @pdata: driver platform data
|
||||
*
|
||||
* Initialize the BQ25792 by disabling the watchdog, enabling discharge
|
||||
* current sensing with 5A limit, and configuring input current regulation.
|
||||
* Set the charge current, charge voltage, minimum system voltage, and
|
||||
* input current limit from platform data. Enable and configure the ADC
|
||||
* to measure all available channels.
|
||||
*
|
||||
* Return: Returns 0 on success or error code on error.
|
||||
*/
|
||||
static int bq25792_hw_init(struct bq257xx_chg *pdata)
|
||||
{
|
||||
struct regmap *regmap = pdata->bq->regmap;
|
||||
int ret = 0;
|
||||
u8 reg;
|
||||
|
||||
/* Disable watchdog (TODO: make it work instead) */
|
||||
ret = regmap_write(regmap, BQ25792_REG10_CHARGER_CONTROL_1, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable battery discharge current sensing, 5A discharge current
|
||||
* limit, input current regulation and ship FET functions
|
||||
*/
|
||||
ret = regmap_write(regmap, BQ25792_REG14_CHARGER_CONTROL_5,
|
||||
BQ25792_REG14_SFET_PRESENT |
|
||||
BQ25792_REG14_EN_IBAT |
|
||||
BQ25792_IBAT_5A |
|
||||
BQ25792_REG14_EN_IINDPM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->vbat_max < 5000000) {
|
||||
/* 1S batteries */
|
||||
reg = FIELD_PREP(BQ25792_REG0A_CELL_MASK, BQ25792_CELL_1S);
|
||||
} else if (pdata->vbat_max < 10000000) {
|
||||
/* 2S batteries */
|
||||
reg = FIELD_PREP(BQ25792_REG0A_CELL_MASK, BQ25792_CELL_2S);
|
||||
} else if (pdata->vbat_max < 14000000) {
|
||||
/* 3S batteries */
|
||||
reg = FIELD_PREP(BQ25792_REG0A_CELL_MASK, BQ25792_CELL_3S);
|
||||
} else {
|
||||
/* 4S batteries */
|
||||
reg = FIELD_PREP(BQ25792_REG0A_CELL_MASK, BQ25792_CELL_4S);
|
||||
}
|
||||
|
||||
/* Recharge voltage detection deglitch time (default 1024ms) */
|
||||
reg |= FIELD_PREP(BQ25792_REG0A_TRECHG_MASK, BQ25792_TRECHG_1024MS);
|
||||
|
||||
/* Recharge voltage offset: 5% of the set charge voltage */
|
||||
reg |= FIELD_PREP(BQ25792_REG0A_VRECHG_MASK,
|
||||
(pdata->vbat_max / 20 - BQ25792_VRECHG_MIN_UV) / BQ25792_VRECHG_STEP_UV);
|
||||
|
||||
ret = regmap_write(regmap, BQ25792_REG0A_RECHARGE_CONTROL, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bq25792_set_min_vsys(pdata, pdata->vsys_min);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pdata->chip->bq257xx_set_iindpm(pdata, pdata->iindpm_max);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable the Input Current Optimizer (the rest is at POR value) */
|
||||
ret = regmap_write(regmap, BQ25792_REG0F_CHARGER_CONTROL_0,
|
||||
BQ25792_REG0F_EN_AUTO_IBATDIS |
|
||||
BQ25792_REG0F_EN_CHG |
|
||||
BQ25792_REG0F_EN_ICO |
|
||||
BQ25792_REG0F_EN_TERM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable the ADC. */
|
||||
ret = regmap_write(regmap, BQ25792_REG2E_ADC_CONTROL, BQ25792_REG2E_ADC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear per-channel ADC disable bits - enable all channels */
|
||||
ret = regmap_write(regmap, BQ25792_REG2F_ADC_FUNCTION_DISABLE_0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(regmap, BQ25792_REG30_ADC_FUNCTION_DISABLE_1, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25703_hw_shutdown() - Set registers for shutdown
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -446,6 +869,30 @@ static void bq25703_hw_shutdown(struct bq257xx_chg *pdata)
|
|||
BQ25703_EN_LWPWR, BQ25703_EN_LWPWR);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq25792_hw_shutdown() - Shutdown BQ25792 hardware
|
||||
* @pdata: driver platform data
|
||||
*
|
||||
* Perform hardware shutdown for the BQ25792. Currently a no-op
|
||||
* as the device does not require special shutdown configuration.
|
||||
*/
|
||||
static void bq25792_hw_shutdown(struct bq257xx_chg *pdata)
|
||||
{
|
||||
/* Nothing to do here */
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_set_charger_property() - Set a power supply property
|
||||
* @psy: power supply device
|
||||
* @prop: power supply property to set
|
||||
* @val: value to set for the property
|
||||
*
|
||||
* Handle requests to set power supply properties such as input current
|
||||
* limit, constant charge voltage, and constant charge current. Routes
|
||||
* the request to the chip-specific implementation.
|
||||
*
|
||||
* Return: Returns 0 on success or -EINVAL if property is not supported.
|
||||
*/
|
||||
static int bq257xx_set_charger_property(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
const union power_supply_propval *val)
|
||||
|
|
@ -469,6 +916,19 @@ static int bq257xx_set_charger_property(struct power_supply *psy,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_get_charger_property() - Get a power supply property
|
||||
* @psy: power supply device
|
||||
* @psp: power supply property to get
|
||||
* @val: pointer to store the property value
|
||||
*
|
||||
* Handle requests to get power supply properties, including status,
|
||||
* health, manufacturer, online state, and various voltage/current
|
||||
* measurements. Reads current device state and routes chip-specific
|
||||
* property requests to appropriate handlers.
|
||||
*
|
||||
* Return: Returns 0 on success or -EINVAL if property is not supported.
|
||||
*/
|
||||
static int bq257xx_get_charger_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
|
@ -550,6 +1010,17 @@ static enum power_supply_property bq257xx_power_supply_props[] = {
|
|||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
};
|
||||
|
||||
/**
|
||||
* bq257xx_property_is_writeable() - Check if a property is writeable
|
||||
* @psy: power supply device
|
||||
* @prop: power supply property to check
|
||||
*
|
||||
* Determines which power supply properties can be written to. Only
|
||||
* charge current limit, charge voltage limit, and input current
|
||||
* limit are writeable.
|
||||
*
|
||||
* Return: Returns 1 if property is writeable, 0 otherwise.
|
||||
*/
|
||||
static int bq257xx_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property prop)
|
||||
{
|
||||
|
|
@ -622,6 +1093,17 @@ static void bq257xx_external_power_changed(struct power_supply *psy)
|
|||
power_supply_changed(psy);
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_irq_handler_thread() - Handle charger interrupt
|
||||
* @irq: interrupt number
|
||||
* @private: pointer to driver private data
|
||||
*
|
||||
* Thread handler for charger interrupts. Triggers re-evaluation of
|
||||
* external power status and updates power supply state in response
|
||||
* to charger events.
|
||||
*
|
||||
* Return: Returns IRQ_HANDLED if interrupt was processed.
|
||||
*/
|
||||
static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct bq257xx_chg *pdata = private;
|
||||
|
|
@ -662,6 +1144,22 @@ static const struct bq257xx_chip_info bq25703_chip_info = {
|
|||
.bq257xx_get_min_vsys = &bq25703_get_min_vsys,
|
||||
};
|
||||
|
||||
static const struct bq257xx_chip_info bq25792_chip_info = {
|
||||
.default_iindpm_uA = BQ25792_IINDPM_DEFAULT_UA,
|
||||
.bq257xx_hw_init = &bq25792_hw_init,
|
||||
.bq257xx_hw_shutdown = &bq25792_hw_shutdown,
|
||||
.bq257xx_get_state = &bq25792_get_state,
|
||||
.bq257xx_get_ichg = &bq25792_get_ichg_cur,
|
||||
.bq257xx_set_ichg = &bq25792_set_ichg_cur,
|
||||
.bq257xx_get_vbatreg = &bq25792_get_chrg_volt,
|
||||
.bq257xx_set_vbatreg = &bq25792_set_chrg_volt,
|
||||
.bq257xx_get_iindpm = &bq25792_get_iindpm,
|
||||
.bq257xx_set_iindpm = &bq25792_set_iindpm,
|
||||
.bq257xx_get_cur = &bq25792_get_cur,
|
||||
.bq257xx_get_vbat = &bq25792_get_vbat,
|
||||
.bq257xx_get_min_vsys = &bq25792_get_min_vsys,
|
||||
};
|
||||
|
||||
/**
|
||||
* bq257xx_parse_dt() - Parse the device tree for required properties
|
||||
* @pdata: driver platform data
|
||||
|
|
@ -707,6 +1205,17 @@ static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_charger_probe() - Probe routine for charger platform device
|
||||
* @pdev: platform device
|
||||
*
|
||||
* Probe the charger device, allocate driver data structure, select the
|
||||
* appropriate chip-specific function pointers, register the power supply,
|
||||
* parse device tree properties for battery limits, initialize hardware,
|
||||
* and set up the interrupt handler if available.
|
||||
*
|
||||
* Return: Returns 0 on success or error code on failure.
|
||||
*/
|
||||
static int bq257xx_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
|
@ -722,7 +1231,17 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pdata->bq = bq;
|
||||
pdata->chip = &bq25703_chip_info;
|
||||
|
||||
switch (bq->type) {
|
||||
case BQ25703A:
|
||||
pdata->chip = &bq25703_chip_info;
|
||||
break;
|
||||
case BQ25792:
|
||||
pdata->chip = &bq25792_chip_info;
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL, "Unknown chip type\n");
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
|
|
@ -753,13 +1272,18 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&bq->client->dev), pdata);
|
||||
if (ret < 0)
|
||||
dev_err_probe(dev, ret, "Charger get irq failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_charger_shutdown() - Shutdown routine for charger platform device
|
||||
* @pdev: platform device
|
||||
*
|
||||
* Called during system shutdown to perform charger cleanup, including
|
||||
* disabling watchdog timers or other chip-specific shutdown procedures.
|
||||
*/
|
||||
static void bq257xx_charger_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct bq257xx_chg *pdata = platform_get_drvdata(pdev);
|
||||
|
|
|
|||
|
|
@ -1389,6 +1389,14 @@ static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bq25890_release_secondary_chrg(void *data)
|
||||
{
|
||||
struct bq25890_device *bq = data;
|
||||
|
||||
power_supply_put(bq->secondary_chrg);
|
||||
bq->secondary_chrg = NULL;
|
||||
}
|
||||
|
||||
static int bq25890_fw_probe(struct bq25890_device *bq)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -1401,6 +1409,10 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
|
|||
bq->secondary_chrg = power_supply_get_by_name(str);
|
||||
if (!bq->secondary_chrg)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = devm_add_action_or_reset(bq->dev, bq25890_release_secondary_chrg, bq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Optional, left at 0 if property is not present */
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ static u8
|
|||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTES] = 0x1a,
|
||||
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_RC] = 0x10,
|
||||
|
|
@ -495,7 +495,7 @@ static u8
|
|||
[BQ27XXX_REG_RC] = 0x10,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x22,
|
||||
|
|
@ -516,7 +516,7 @@ static u8
|
|||
[BQ27XXX_REG_RC] = 0x10,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x22,
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,29 @@ static int charger_extcon_init(struct charger_manager *cm,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int charger_manager_get_regulators(struct charger_manager *cm)
|
||||
{
|
||||
struct charger_desc *desc = cm->desc;
|
||||
struct charger_regulator *charger;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
charger = &desc->charger_regulators[i];
|
||||
charger->consumer = regulator_get(cm->dev,
|
||||
charger->regulator_name);
|
||||
if (IS_ERR(charger->consumer)) {
|
||||
dev_err(cm->dev, "Cannot find charger(%s)\n",
|
||||
charger->regulator_name);
|
||||
ret = PTR_ERR(charger->consumer);
|
||||
while (i-- > 0)
|
||||
regulator_put(desc->charger_regulators[i].consumer);
|
||||
return ret;
|
||||
}
|
||||
charger->cm = cm;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* charger_manager_register_extcon - Register extcon device to receive state
|
||||
* of charger cable.
|
||||
|
|
@ -1036,15 +1059,6 @@ static int charger_manager_register_extcon(struct charger_manager *cm)
|
|||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
charger = &desc->charger_regulators[i];
|
||||
|
||||
charger->consumer = regulator_get(cm->dev,
|
||||
charger->regulator_name);
|
||||
if (IS_ERR(charger->consumer)) {
|
||||
dev_err(cm->dev, "Cannot find charger(%s)\n",
|
||||
charger->regulator_name);
|
||||
return PTR_ERR(charger->consumer);
|
||||
}
|
||||
charger->cm = cm;
|
||||
|
||||
for (j = 0; j < charger->num_cables; j++) {
|
||||
struct charger_cable *cable = &charger->cables[j];
|
||||
|
||||
|
|
@ -1580,13 +1594,23 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
}
|
||||
psy_cfg.attr_grp = desc->sysfs_groups;
|
||||
|
||||
/*
|
||||
* Acquire charger regulators before exposing the sysfs entries, so
|
||||
* userspace cannot reach externally_control before the regulators
|
||||
* (and charger->cm) are available. Mirrors the order in remove().
|
||||
*/
|
||||
ret = charger_manager_get_regulators(cm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cm->charger_psy = power_supply_register(&pdev->dev,
|
||||
&cm->charger_psy_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(cm->charger_psy)) {
|
||||
dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
|
||||
cm->charger_psy_desc.name);
|
||||
return PTR_ERR(cm->charger_psy);
|
||||
ret = PTR_ERR(cm->charger_psy);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
/* Register extcon device for charger cable */
|
||||
|
|
@ -1620,11 +1644,11 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err_reg_extcon:
|
||||
power_supply_unregister(cm->charger_psy);
|
||||
err_regulator:
|
||||
for (i = 0; i < desc->num_charger_regulators; i++)
|
||||
regulator_put(desc->charger_regulators[i].consumer);
|
||||
|
||||
power_supply_unregister(cm->charger_psy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1642,12 +1666,12 @@ static void charger_manager_remove(struct platform_device *pdev)
|
|||
cancel_work_sync(&setup_polling);
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++)
|
||||
regulator_put(desc->charger_regulators[i].consumer);
|
||||
try_charger_enable(cm, false);
|
||||
|
||||
power_supply_unregister(cm->charger_psy);
|
||||
|
||||
try_charger_enable(cm, false);
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++)
|
||||
regulator_put(desc->charger_regulators[i].consumer);
|
||||
}
|
||||
|
||||
static const struct platform_device_id charger_manager_id[] = {
|
||||
|
|
|
|||
|
|
@ -957,12 +957,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev,
|
|||
cpcap_battery_irq_thread,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
name, ddata);
|
||||
if (error) {
|
||||
dev_err(ddata->dev, "could not get irq %s: %i\n",
|
||||
name, error);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
|
|
|
|||
|
|
@ -761,12 +761,8 @@ static int cpcap_usb_init_irq(struct platform_device *pdev,
|
|||
cpcap_charger_irq_thread,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
name, ddata);
|
||||
if (error) {
|
||||
dev_err(ddata->dev, "could not get irq %s: %i\n",
|
||||
name, error);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
|
|
|
|||
|
|
@ -258,6 +258,14 @@ static int cros_ec_notify(struct notifier_block *nb,
|
|||
return cros_pchg_event(charger);
|
||||
}
|
||||
|
||||
static void cros_pchg_unregister_notifier(void *data)
|
||||
{
|
||||
struct charger_data *charger = data;
|
||||
|
||||
blocking_notifier_chain_unregister(&charger->ec_device->event_notifier,
|
||||
&charger->notifier);
|
||||
}
|
||||
|
||||
static int cros_pchg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
|
@ -345,9 +353,10 @@ static int cros_pchg_probe(struct platform_device *pdev)
|
|||
ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
|
||||
nb);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
|
||||
return dev_err_probe(dev, ret, "Failed to register notifier\n");
|
||||
|
||||
return 0;
|
||||
return devm_add_action_or_reset(dev, cros_pchg_unregister_notifier,
|
||||
charger);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
|
|||
|
|
@ -124,6 +124,11 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (resp.port_count > EC_USB_PD_MAX_PORTS) {
|
||||
dev_warn(charger->dev, "Charge port count out of bounds\n");
|
||||
return EC_USB_PD_MAX_PORTS;
|
||||
}
|
||||
|
||||
return resp.port_count;
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +142,11 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (resp.num_ports > EC_USB_PD_MAX_PORTS) {
|
||||
dev_warn(charger->dev, "USB PD port count out of bounds\n");
|
||||
return EC_USB_PD_MAX_PORTS;
|
||||
}
|
||||
|
||||
return resp.num_ports;
|
||||
}
|
||||
|
||||
|
|
@ -588,10 +598,13 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
|
|||
|
||||
/*
|
||||
* Sanity checks on the number of ports:
|
||||
* there should be at most 1 dedicated port
|
||||
* there should be at most 1 dedicated port, and the count must
|
||||
* not exceed the maximum number of supported ports
|
||||
* (EC_USB_PD_MAX_PORTS).
|
||||
*/
|
||||
if (charger->num_charger_ports < charger->num_usbpd_ports ||
|
||||
charger->num_charger_ports > (charger->num_usbpd_ports + 1)) {
|
||||
charger->num_charger_ports > (charger->num_usbpd_ports + 1) ||
|
||||
charger->num_charger_ports > EC_USB_PD_MAX_PORTS) {
|
||||
dev_err(dev, "Unexpected number of charge port count\n");
|
||||
ret = -EPROTO;
|
||||
goto fail_nowarn;
|
||||
|
|
|
|||
|
|
@ -736,6 +736,7 @@ static const struct i2c_device_id cw_bat_id_table[] = {
|
|||
{ .name = "cw2015" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cw_bat_id_table);
|
||||
|
||||
static const struct of_device_id cw2015_of_match[] = {
|
||||
{ .compatible = "cellwise,cw2015" },
|
||||
|
|
|
|||
|
|
@ -524,10 +524,8 @@ static int da9150_fg_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
|
||||
IRQF_ONESHOT, "FG", fg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ static int gab_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"battery charged", adc_bat);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to register irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, adc_bat);
|
||||
|
|
|
|||
|
|
@ -482,6 +482,7 @@ static void isp1704_charger_remove(struct platform_device *pdev)
|
|||
struct isp1704_charger *isp = platform_get_drvdata(pdev);
|
||||
|
||||
usb_unregister_notifier(isp->phy, &isp->nb);
|
||||
cancel_work_sync(&isp->work);
|
||||
power_supply_unregister(isp->psy);
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,10 +280,10 @@ static int lp8727_setup_irq(struct lp8727_chg *pchg)
|
|||
|
||||
static void lp8727_release_irq(struct lp8727_chg *pchg)
|
||||
{
|
||||
cancel_delayed_work_sync(&pchg->work);
|
||||
|
||||
if (pchg->irq)
|
||||
free_irq(pchg->irq, pchg);
|
||||
|
||||
cancel_delayed_work_sync(&pchg->work);
|
||||
}
|
||||
|
||||
static enum power_supply_property lp8727_charger_prop[] = {
|
||||
|
|
|
|||
|
|
@ -710,8 +710,8 @@ static void lp8788_charger_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct lp8788_charger *pchg = platform_get_drvdata(pdev);
|
||||
|
||||
flush_work(&pchg->charger_work);
|
||||
lp8788_irq_unregister(pdev, pchg);
|
||||
cancel_work_sync(&pchg->charger_work);
|
||||
}
|
||||
|
||||
static struct platform_driver lp8788_charger_driver = {
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client)
|
|||
|
||||
/* r_sense can be negative, when sense+ is connected to the battery
|
||||
* instead of the sense-. This results in reversed measurements. */
|
||||
ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
|
||||
ret = of_property_read_s32(np, "lltc,resistor-sense", &r_sense);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Could not find lltc,resistor-sense in devicetree\n");
|
||||
|
|
|
|||
|
|
@ -287,10 +287,8 @@ static int max14656_probe(struct i2c_client *client)
|
|||
ret = devm_request_irq(dev, chip->irq, max14656_irq,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
MAX14656_NAME, chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "request_irq %d failed\n", chip->irq);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
}
|
||||
enable_irq_wake(chip->irq);
|
||||
|
||||
schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(2000));
|
||||
|
|
|
|||
|
|
@ -192,8 +192,11 @@ static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)
|
|||
static int max17040_get_vcell(struct max17040_chip *chip)
|
||||
{
|
||||
u32 vcell;
|
||||
int ret;
|
||||
|
||||
regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
|
||||
ret = regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return max17040_raw_vcell_to_uvolts(chip, vcell);
|
||||
}
|
||||
|
|
@ -201,8 +204,11 @@ static int max17040_get_vcell(struct max17040_chip *chip)
|
|||
static int max17040_get_soc(struct max17040_chip *chip)
|
||||
{
|
||||
u32 soc;
|
||||
int ret;
|
||||
|
||||
regmap_read(chip->regmap, MAX17040_SOC, &soc);
|
||||
ret = regmap_read(chip->regmap, MAX17040_SOC, &soc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return soc >> (chip->quirk_double_soc ? 9 : 8);
|
||||
}
|
||||
|
|
@ -261,7 +267,11 @@ static int max17040_get_of_data(struct max17040_chip *chip)
|
|||
|
||||
static void max17040_check_changes(struct max17040_chip *chip)
|
||||
{
|
||||
chip->soc = max17040_get_soc(chip);
|
||||
int soc;
|
||||
|
||||
soc = max17040_get_soc(chip);
|
||||
if (soc >= 0)
|
||||
chip->soc = soc;
|
||||
}
|
||||
|
||||
static void max17040_queue_work(struct max17040_chip *chip)
|
||||
|
|
@ -396,10 +406,16 @@ static int max17040_get_property(struct power_supply *psy,
|
|||
val->intval = max17040_get_online(chip);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
val->intval = max17040_get_vcell(chip);
|
||||
ret = max17040_get_vcell(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = max17040_get_soc(chip);
|
||||
ret = max17040_get_soc(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||
val->intval = chip->low_soc_alert;
|
||||
|
|
@ -458,16 +474,12 @@ static const struct power_supply_desc max17040_battery_desc = {
|
|||
static int max17040_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max17040_chip *chip;
|
||||
enum chip_id chip_id;
|
||||
bool enable_irq = false;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
return -EIO;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
|
@ -568,7 +580,7 @@ static int max17040_suspend(struct device *dev)
|
|||
// disable soc alert to prevent wakeup
|
||||
max17040_set_soc_alert(chip, 0);
|
||||
else
|
||||
cancel_delayed_work(&chip->work);
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
|
||||
if (client->irq && device_may_wakeup(dev))
|
||||
enable_irq_wake(client->irq);
|
||||
|
|
|
|||
|
|
@ -62,18 +62,32 @@
|
|||
#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */
|
||||
#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */
|
||||
|
||||
#define MAX17055_DQACC_DIV 32
|
||||
#define MAX17055_DPACC_FACTOR 44138
|
||||
#define MAX17055_DPACC_VCHG_FACTOR 51200
|
||||
#define MAX17055_FSTAT_DNR_BIT BIT(0)
|
||||
#define MAX17055_VCHG_THRESHOLD_UV 4275000
|
||||
#define MAX17055_DNR_POLL_US 10000
|
||||
#define MAX17055_DNR_TIMEOUT_US 2000000
|
||||
#define MAX17055_INIT_RETRY_DELAY_MS 10000
|
||||
#define MAX17055_REFRESH_POLL_US 10000
|
||||
#define MAX17055_REFRESH_TIMEOUT_US 1000000
|
||||
|
||||
struct max17042_chip {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct power_supply *battery;
|
||||
enum max170xx_chip_type chip_type;
|
||||
struct max17042_config_data *config_data;
|
||||
struct work_struct work;
|
||||
int init_complete;
|
||||
struct delayed_work work;
|
||||
int irq;
|
||||
int task_period;
|
||||
bool enable_current_sense;
|
||||
bool enable_por_init;
|
||||
bool enable_vchg_override;
|
||||
bool init_complete;
|
||||
bool hib_restore_pending;
|
||||
u16 hib_cfg;
|
||||
unsigned int r_sns;
|
||||
int vmin; /* in millivolts */
|
||||
int vmax; /* in millivolts */
|
||||
|
|
@ -256,7 +270,7 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
u32 data;
|
||||
u64 data64;
|
||||
|
||||
if (!chip->init_complete)
|
||||
if (!READ_ONCE(chip->init_complete))
|
||||
return -EAGAIN;
|
||||
|
||||
switch (psp) {
|
||||
|
|
@ -566,6 +580,24 @@ static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int max17055_write_verify_reg(struct regmap *map, u8 reg, u32 value)
|
||||
{
|
||||
u32 read_value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(map, reg, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
ret = regmap_read(map, reg, &read_value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return read_value == value ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static inline void max17042_override_por(struct regmap *map,
|
||||
u8 reg, u16 value)
|
||||
{
|
||||
|
|
@ -801,8 +833,12 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
max17042_override_por(map, MAX17042_CONFIG, config->config);
|
||||
max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
|
||||
|
||||
max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
|
||||
max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
|
||||
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) {
|
||||
max17042_override_por(map, MAX17042_DesignCap,
|
||||
config->design_cap);
|
||||
max17042_override_por(map, MAX17042_ICHGTerm,
|
||||
config->ichgt_term);
|
||||
}
|
||||
|
||||
max17042_override_por(map, MAX17042_AtRate, config->at_rate);
|
||||
max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
|
||||
|
|
@ -812,8 +848,10 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
|
||||
max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
|
||||
max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
|
||||
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
|
||||
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) {
|
||||
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
|
||||
}
|
||||
|
||||
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
|
||||
|
|
@ -843,30 +881,183 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
max17042_override_por(map, MAX17047_V_empty, config->vempty);
|
||||
}
|
||||
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 &&
|
||||
!chip->enable_vchg_override)
|
||||
max17042_override_por(map, MAX17055_ModelCfg, config->model_cfg);
|
||||
}
|
||||
|
||||
static int max17055_override_battery_values(struct max17042_chip *chip)
|
||||
{
|
||||
struct max17042_config_data *config = chip->config_data;
|
||||
struct regmap *map = chip->regmap;
|
||||
unsigned int design_cap;
|
||||
unsigned int model_cfg;
|
||||
unsigned int dqacc;
|
||||
u64 dpacc;
|
||||
int ret;
|
||||
|
||||
if (config->design_cap) {
|
||||
ret = max17055_write_verify_reg(map, MAX17042_DesignCap,
|
||||
config->design_cap);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (config->dqacc) {
|
||||
ret = max17055_write_verify_reg(map, MAX17042_dQacc,
|
||||
config->dqacc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (config->ichgt_term) {
|
||||
ret = max17055_write_verify_reg(map, MAX17042_ICHGTerm,
|
||||
config->ichgt_term);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chip->enable_vchg_override) {
|
||||
ret = regmap_update_bits(map, MAX17055_ModelCfg,
|
||||
MAX17055_MODELCFG_VCHG_BIT,
|
||||
config->model_cfg &
|
||||
MAX17055_MODELCFG_VCHG_BIT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
if (!config->design_cap && !config->dqacc &&
|
||||
!chip->enable_vchg_override)
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(map, MAX17042_DesignCap, &design_cap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(map, MAX17042_dQacc, &dqacc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(map, MAX17055_ModelCfg, &model_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (chip->enable_vchg_override &&
|
||||
(model_cfg & MAX17055_MODELCFG_VCHG_BIT) !=
|
||||
(config->model_cfg & MAX17055_MODELCFG_VCHG_BIT))
|
||||
return -EIO;
|
||||
|
||||
if (!design_cap || !dqacc)
|
||||
return -ERANGE;
|
||||
|
||||
dpacc = (u64)dqacc *
|
||||
(model_cfg & MAX17055_MODELCFG_VCHG_BIT ?
|
||||
MAX17055_DPACC_VCHG_FACTOR : MAX17055_DPACC_FACTOR);
|
||||
do_div(dpacc, design_cap);
|
||||
if (dpacc > U16_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
return max17055_write_verify_reg(map, MAX17042_dPacc, (u16)dpacc);
|
||||
}
|
||||
|
||||
static int max17055_restore_hibernate(struct max17042_chip *chip)
|
||||
{
|
||||
int restore_hib_ret;
|
||||
int soft_wakeup_ret;
|
||||
|
||||
soft_wakeup_ret = regmap_write(chip->regmap, MAX17055_SoftWakeup, 0);
|
||||
restore_hib_ret = max17055_write_verify_reg(chip->regmap,
|
||||
MAX17055_HibCfg,
|
||||
chip->hib_cfg);
|
||||
if (!soft_wakeup_ret && !restore_hib_ret)
|
||||
chip->hib_restore_pending = false;
|
||||
|
||||
return soft_wakeup_ret ?: restore_hib_ret;
|
||||
}
|
||||
|
||||
static int max17055_init_chip(struct max17042_chip *chip)
|
||||
{
|
||||
struct regmap *map = chip->regmap;
|
||||
unsigned int hib_cfg;
|
||||
unsigned int model_cfg;
|
||||
unsigned int fstat;
|
||||
int restore_ret;
|
||||
int ret;
|
||||
|
||||
if (chip->hib_restore_pending) {
|
||||
ret = max17055_restore_hibernate(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read_poll_timeout(map, MAX17042_FSTAT, fstat,
|
||||
!(fstat & MAX17055_FSTAT_DNR_BIT),
|
||||
MAX17055_DNR_POLL_US,
|
||||
MAX17055_DNR_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(map, MAX17055_HibCfg, &hib_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->hib_cfg = hib_cfg;
|
||||
chip->hib_restore_pending = true;
|
||||
|
||||
ret = regmap_write(map, MAX17055_SoftWakeup, 0x0090);
|
||||
if (ret)
|
||||
goto restore_hibernate;
|
||||
|
||||
ret = max17055_write_verify_reg(map, MAX17055_HibCfg, 0);
|
||||
if (ret)
|
||||
goto restore_hibernate;
|
||||
|
||||
ret = regmap_write(map, MAX17055_SoftWakeup, 0);
|
||||
if (ret)
|
||||
goto restore_hibernate;
|
||||
|
||||
max17042_override_por_values(chip);
|
||||
|
||||
ret = max17055_override_battery_values(chip);
|
||||
if (ret)
|
||||
goto restore_hibernate;
|
||||
|
||||
ret = regmap_write_bits(map, MAX17055_ModelCfg,
|
||||
MAX17055_MODELCFG_REFRESH_BIT,
|
||||
MAX17055_MODELCFG_REFRESH_BIT);
|
||||
if (ret)
|
||||
goto restore_hibernate;
|
||||
|
||||
ret = regmap_read_poll_timeout(map, MAX17055_ModelCfg, model_cfg,
|
||||
!(model_cfg &
|
||||
MAX17055_MODELCFG_REFRESH_BIT),
|
||||
MAX17055_REFRESH_POLL_US,
|
||||
MAX17055_REFRESH_TIMEOUT_US);
|
||||
|
||||
restore_hibernate:
|
||||
restore_ret = max17055_restore_hibernate(chip);
|
||||
if (restore_ret)
|
||||
return restore_ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max17042_init_chip(struct max17042_chip *chip)
|
||||
{
|
||||
struct regmap *map = chip->regmap;
|
||||
int ret;
|
||||
|
||||
max17042_override_por_values(chip);
|
||||
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
|
||||
regmap_write_bits(map, MAX17055_ModelCfg,
|
||||
MAX17055_MODELCFG_REFRESH_BIT,
|
||||
MAX17055_MODELCFG_REFRESH_BIT);
|
||||
}
|
||||
ret = max17055_init_chip(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
max17042_override_por_values(chip);
|
||||
|
||||
/* After Power up, the MAX17042 requires 500mS in order
|
||||
* to perform signal debouncing and initial SOC reporting
|
||||
*/
|
||||
msleep(500);
|
||||
/* Allow signal debouncing and initial SOC reporting. */
|
||||
msleep(500);
|
||||
|
||||
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) {
|
||||
/* Initialize configuration */
|
||||
max17042_write_config_regs(chip);
|
||||
|
||||
/* write cell characterization data */
|
||||
|
|
@ -902,8 +1093,7 @@ static int max17042_init_chip(struct max17042_chip *chip)
|
|||
}
|
||||
|
||||
/* Init complete, Clear the POR bit */
|
||||
regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
|
||||
return 0;
|
||||
return regmap_clear_bits(map, MAX17042_STATUS, STATUS_POR_BIT);
|
||||
}
|
||||
|
||||
static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
|
||||
|
|
@ -989,18 +1179,28 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
|
|||
|
||||
static void max17042_init_worker(struct work_struct *work)
|
||||
{
|
||||
struct max17042_chip *chip = container_of(work,
|
||||
struct max17042_chip *chip = container_of(to_delayed_work(work),
|
||||
struct max17042_chip, work);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Initialize registers according to values from config_data */
|
||||
if (chip->enable_por_init && chip->config_data) {
|
||||
if (chip->enable_por_init && chip->config_data)
|
||||
ret = max17042_init_chip(chip);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (ret) {
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
|
||||
dev_warn_ratelimited(chip->dev,
|
||||
"initialization failed: %d, retrying\n", ret);
|
||||
schedule_delayed_work(&chip->work,
|
||||
msecs_to_jiffies(MAX17055_INIT_RETRY_DELAY_MS));
|
||||
} else {
|
||||
dev_err(chip->dev, "initialization failed: %d\n", ret);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
chip->init_complete = 1;
|
||||
WRITE_ONCE(chip->init_complete, true);
|
||||
power_supply_changed(chip->battery);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
@ -1026,10 +1226,14 @@ static int max17042_parse_dt(struct max17042_chip *chip)
|
|||
chip->temp_min = INT_MIN;
|
||||
if (of_property_read_s32(np, "maxim,over-heat-temp", &chip->temp_max))
|
||||
chip->temp_max = INT_MAX;
|
||||
if (of_property_read_s32(np, "maxim,dead-volt", &chip->vmin))
|
||||
if (of_property_read_u32(np, "maxim,dead-volt", &prop))
|
||||
chip->vmin = INT_MIN;
|
||||
if (of_property_read_s32(np, "maxim,over-volt", &chip->vmax))
|
||||
else
|
||||
chip->vmin = prop;
|
||||
if (of_property_read_u32(np, "maxim,over-volt", &prop))
|
||||
chip->vmax = INT_MAX;
|
||||
else
|
||||
chip->vmin = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1060,6 +1264,102 @@ static int max17042_init_defaults(struct max17042_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_apply_battery_properties(struct max17042_chip *chip,
|
||||
struct power_supply_battery_info *info)
|
||||
{
|
||||
struct max17042_config_data *config;
|
||||
struct device *dev = chip->dev;
|
||||
bool have_design_cap;
|
||||
bool have_ichgt_term;
|
||||
bool have_vchg;
|
||||
u16 design_cap = 0;
|
||||
u16 ichgt_term = 0;
|
||||
u16 dqacc = 0;
|
||||
u16 model_cfg = 0;
|
||||
u64 data64;
|
||||
|
||||
if (!info || chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055)
|
||||
return 0;
|
||||
|
||||
have_design_cap = chip->enable_current_sense &&
|
||||
info->charge_full_design_uah > 0;
|
||||
have_ichgt_term = chip->enable_current_sense &&
|
||||
info->charge_term_current_ua > 0;
|
||||
have_vchg = info->voltage_max_design_uv >= 0;
|
||||
if (!have_design_cap && !have_ichgt_term && !have_vchg)
|
||||
return 0;
|
||||
|
||||
if (have_design_cap) {
|
||||
data64 = (u64)info->charge_full_design_uah * chip->r_sns;
|
||||
do_div(data64, MAX17042_CAPACITY_LSB);
|
||||
if (!data64)
|
||||
return dev_err_probe(dev, -ERANGE,
|
||||
"battery design capacity is too small for sense resistor\n");
|
||||
if (data64 > U16_MAX)
|
||||
return dev_err_probe(dev, -ERANGE,
|
||||
"battery design capacity exceeds register range\n");
|
||||
|
||||
design_cap = (u16)data64;
|
||||
dqacc = design_cap / MAX17055_DQACC_DIV;
|
||||
if (!dqacc)
|
||||
return dev_err_probe(dev, -ERANGE,
|
||||
"battery design capacity is too small for EZ config\n");
|
||||
}
|
||||
|
||||
if (have_ichgt_term) {
|
||||
data64 = (u64)info->charge_term_current_ua * chip->r_sns;
|
||||
do_div(data64, MAX17042_CURRENT_LSB);
|
||||
if (!data64)
|
||||
return dev_err_probe(dev, -ERANGE,
|
||||
"charge termination current is too small for sense resistor\n");
|
||||
if (data64 > S16_MAX)
|
||||
return dev_err_probe(dev, -ERANGE,
|
||||
"charge termination current exceeds positive register range\n");
|
||||
|
||||
ichgt_term = (u16)data64;
|
||||
}
|
||||
|
||||
if (have_vchg) {
|
||||
if (!info->voltage_max_design_uv)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"battery design voltage must be positive\n");
|
||||
|
||||
if (info->voltage_max_design_uv > MAX17055_VCHG_THRESHOLD_UV)
|
||||
model_cfg = MAX17055_MODELCFG_VCHG_BIT;
|
||||
}
|
||||
|
||||
config = chip->config_data;
|
||||
if (!config) {
|
||||
config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (have_design_cap) {
|
||||
config->design_cap = design_cap;
|
||||
config->dqacc = dqacc;
|
||||
}
|
||||
if (have_ichgt_term)
|
||||
config->ichgt_term = ichgt_term;
|
||||
if (have_vchg) {
|
||||
config->model_cfg &= ~MAX17055_MODELCFG_VCHG_BIT;
|
||||
config->model_cfg |= model_cfg;
|
||||
chip->enable_vchg_override = true;
|
||||
}
|
||||
|
||||
chip->config_data = config;
|
||||
chip->enable_por_init = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_init_battery(struct power_supply *psy)
|
||||
{
|
||||
struct max17042_chip *chip = power_supply_get_drvdata(psy);
|
||||
|
||||
return max17042_apply_battery_properties(chip, psy->battery_info);
|
||||
}
|
||||
|
||||
static const struct regmap_config max17042_regmap_config = {
|
||||
.name = "max17042",
|
||||
.reg_bits = 8,
|
||||
|
|
@ -1113,6 +1413,7 @@ static const struct power_supply_desc max17042_psy_desc = {
|
|||
.set_property = max17042_set_property,
|
||||
.property_is_writeable = max17042_property_is_writeable,
|
||||
.external_power_changed = power_supply_changed,
|
||||
.init = max17042_init_battery,
|
||||
.properties = max17042_battery_props,
|
||||
.num_properties = ARRAY_SIZE(max17042_battery_props),
|
||||
};
|
||||
|
|
@ -1123,6 +1424,7 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
|
|||
.get_property = max17042_get_property,
|
||||
.set_property = max17042_set_property,
|
||||
.property_is_writeable = max17042_property_is_writeable,
|
||||
.init = max17042_init_battery,
|
||||
.properties = max17042_battery_props,
|
||||
.num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
|
||||
};
|
||||
|
|
@ -1242,15 +1544,18 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
|
|||
|
||||
chip->irq = irq;
|
||||
|
||||
regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
ret = regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to read status\n");
|
||||
|
||||
if (val & STATUS_POR_BIT) {
|
||||
ret = devm_work_autocancel(dev, &chip->work,
|
||||
max17042_init_worker);
|
||||
ret = devm_delayed_work_autocancel(dev, &chip->work,
|
||||
max17042_init_worker);
|
||||
if (ret)
|
||||
return ret;
|
||||
schedule_work(&chip->work);
|
||||
schedule_delayed_work(&chip->work, 0);
|
||||
} else {
|
||||
chip->init_complete = 1;
|
||||
WRITE_ONCE(chip->init_complete, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -532,9 +532,7 @@ static int max77759_init_irqhandler(struct max77759_charger *chg)
|
|||
ret = devm_request_threaded_irq(dev, chg->irqs[i], NULL,
|
||||
thread_fn, 0, name, chg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Unable to register irq handler for %s\n",
|
||||
chgr_irqs_str[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -9,11 +9,29 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* IUSB pin: hardcoded by silicon to 100 mA (low) / 500 mA (high).
|
||||
* MAX8903A/B/C/D/E/F/G/H/I datasheet, "Pin Description" table:
|
||||
* "USB Current-Limit Set Input. Drive IUSB logic-low to set the
|
||||
* USB current limit to 100mA. Drive IUSB logic-high to set the
|
||||
* USB current limit to 500mA."
|
||||
* Not a board parameter - never DT-configurable.
|
||||
*/
|
||||
#define MAX8903_USB_CURRENT_LIMIT_LOW_UA 100000
|
||||
#define MAX8903_USB_CURRENT_LIMIT_HIGH_UA 500000
|
||||
|
||||
struct max8903_current_limit_mapping {
|
||||
u32 limit_ua; /* Current limit in microamps */
|
||||
u32 gpio_value; /* GPIO bit pattern */
|
||||
};
|
||||
|
||||
struct max8903_data {
|
||||
struct device *dev;
|
||||
struct power_supply *psy;
|
||||
|
|
@ -31,6 +49,25 @@ struct max8903_data {
|
|||
struct gpio_desc *flt; /* Fault output */
|
||||
struct gpio_desc *dcm; /* Current-Limit Mode input (1: DC, 2: USB) */
|
||||
struct gpio_desc *usus; /* USB Suspend Input (1: suspended) */
|
||||
|
||||
/* DC current limit control (ISET pins) */
|
||||
struct gpio_descs *dc_current_limit_gpios;
|
||||
struct max8903_current_limit_mapping *dc_current_limit_map;
|
||||
u32 dc_current_limit_map_size;
|
||||
u32 dc_current_limit_ua; /* Current setting in uA */
|
||||
|
||||
/* USB current limit control (IUSB pin) */
|
||||
struct gpio_desc *usb_current_limit_gpio;
|
||||
u32 usb_current_limit_ua; /* Current setting in uA */
|
||||
|
||||
/*
|
||||
* Serialises ta_in / usb_in updates against
|
||||
* max8903_set_property() which steers the current-limit write to
|
||||
* the DC or USB path based on which source is currently online.
|
||||
* The IRQ handlers are requested with IRQF_ONESHOT (threaded), so
|
||||
* a sleepable mutex is the right primitive in both contexts.
|
||||
*/
|
||||
struct mutex source_lock;
|
||||
bool fault;
|
||||
bool usb_in;
|
||||
bool ta_in;
|
||||
|
|
@ -40,6 +77,7 @@ static enum power_supply_property max8903_charger_props[] = {
|
|||
POWER_SUPPLY_PROP_STATUS, /* Charger status output */
|
||||
POWER_SUPPLY_PROP_ONLINE, /* External power source */
|
||||
POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, /* Input current limit */
|
||||
};
|
||||
|
||||
static int max8903_get_property(struct power_supply *psy,
|
||||
|
|
@ -47,6 +85,24 @@ static int max8903_get_property(struct power_supply *psy,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
struct max8903_data *data = power_supply_get_drvdata(psy);
|
||||
bool ta_in, usb_in;
|
||||
u32 dc_limit, usb_limit;
|
||||
|
||||
/*
|
||||
* Snapshot the source flags and current-limit settings under the
|
||||
* source_lock that the IRQs (max8903_dcin / max8903_usbin) and
|
||||
* max8903_set_property() take when updating them, so we never
|
||||
* observe a torn pair of (source-online flag, current-limit ua).
|
||||
* The gpiod_get_value() reads further down deliberately stay
|
||||
* outside the lock — they hit the GPIO controller, not driver
|
||||
* state, and the IRQs do not touch them under the lock either.
|
||||
*/
|
||||
mutex_lock(&data->source_lock);
|
||||
ta_in = data->ta_in;
|
||||
usb_in = data->usb_in;
|
||||
dc_limit = data->dc_current_limit_ua;
|
||||
usb_limit = data->usb_current_limit_ua;
|
||||
mutex_unlock(&data->source_lock);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
|
|
@ -55,21 +111,42 @@ static int max8903_get_property(struct power_supply *psy,
|
|||
if (gpiod_get_value(data->chg))
|
||||
/* CHG asserted */
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else if (data->usb_in || data->ta_in)
|
||||
else if (usb_in || ta_in)
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = 0;
|
||||
if (data->usb_in || data->ta_in)
|
||||
val->intval = 1;
|
||||
val->intval = (ta_in || usb_in) ? 1 : 0;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
if (data->fault)
|
||||
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
/*
|
||||
* data->fault is a single bool toggled from one IRQ
|
||||
* handler, so a torn read is not possible; no need to
|
||||
* extend source_lock coverage here.
|
||||
*/
|
||||
val->intval = data->fault ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
|
||||
: POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
/*
|
||||
* Hardware prioritises DC over USB - when ta_in is asserted
|
||||
* the part draws from the DC input regardless of USB state.
|
||||
* So always report the DC-side limit when DC is online, and
|
||||
* refuse rather than silently fall back to the USB cap if
|
||||
* the DC GPIOs are not configured - that would mis-describe
|
||||
* the active source. Same policy applies in the set path.
|
||||
*/
|
||||
if (ta_in) {
|
||||
if (!data->dc_current_limit_gpios)
|
||||
return -ENODATA;
|
||||
val->intval = dc_limit;
|
||||
} else if (usb_in && data->usb_current_limit_gpio) {
|
||||
val->intval = usb_limit;
|
||||
} else {
|
||||
return -ENODATA;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -78,6 +155,144 @@ static int max8903_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max8903_set_dc_current_limit(struct max8903_data *data, u32 limit_ua)
|
||||
{
|
||||
int i, best_idx = -1;
|
||||
/*
|
||||
* The mapping's gpio_value fits in the lowest ndescs bits of one
|
||||
* unsigned long (parse_dc_current_limit enforces ndescs < 32 and
|
||||
* gpio_value < BIT(ndescs)); a single-word bitmap is sufficient
|
||||
* on both 32- and 64-bit builds. Don't use bitmap_from_arr32() -
|
||||
* that macro reinterprets its source pointer as unsigned long on
|
||||
* 64-bit and would read past the on-stack u32.
|
||||
*/
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(u32));
|
||||
|
||||
if (!data->dc_current_limit_gpios)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* Find the highest supported current <= requested. Use a -1
|
||||
* "not found" sentinel rather than tracking best_limit > 0 so
|
||||
* that a 0 uA entry (used to disable charging) can be selected
|
||||
* by a 0 uA request.
|
||||
*/
|
||||
for (i = 0; i < data->dc_current_limit_map_size; i++) {
|
||||
if (data->dc_current_limit_map[i].limit_ua > limit_ua)
|
||||
continue;
|
||||
if (best_idx < 0 ||
|
||||
data->dc_current_limit_map[i].limit_ua >
|
||||
data->dc_current_limit_map[best_idx].limit_ua)
|
||||
best_idx = i;
|
||||
}
|
||||
|
||||
if (best_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
bitmap_zero(values, BITS_PER_TYPE(u32));
|
||||
values[0] = data->dc_current_limit_map[best_idx].gpio_value;
|
||||
gpiod_set_array_value_cansleep(data->dc_current_limit_gpios->ndescs,
|
||||
data->dc_current_limit_gpios->desc,
|
||||
data->dc_current_limit_gpios->info,
|
||||
values);
|
||||
|
||||
data->dc_current_limit_ua = data->dc_current_limit_map[best_idx].limit_ua;
|
||||
dev_dbg(data->dev, "DC current limit set to %u uA\n",
|
||||
data->dc_current_limit_ua);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8903_set_usb_current_limit(struct max8903_data *data, u32 limit_ua)
|
||||
{
|
||||
u32 selected;
|
||||
int gpio_val;
|
||||
|
||||
if (!data->usb_current_limit_gpio)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* IUSB is a single-bit input with two silicon-fixed settings;
|
||||
* pick HIGH (500 mA) iff the caller's cap can absorb it, else
|
||||
* LOW (100 mA), else refuse rather than program a higher current
|
||||
* than the request allows.
|
||||
*/
|
||||
if (limit_ua >= MAX8903_USB_CURRENT_LIMIT_HIGH_UA) {
|
||||
selected = MAX8903_USB_CURRENT_LIMIT_HIGH_UA;
|
||||
gpio_val = 1;
|
||||
} else if (limit_ua >= MAX8903_USB_CURRENT_LIMIT_LOW_UA) {
|
||||
selected = MAX8903_USB_CURRENT_LIMIT_LOW_UA;
|
||||
gpio_val = 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(data->usb_current_limit_gpio, gpio_val);
|
||||
data->usb_current_limit_ua = selected;
|
||||
|
||||
dev_dbg(data->dev, "USB current limit set to %u uA\n",
|
||||
data->usb_current_limit_ua);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8903_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct max8903_data *data = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
/*
|
||||
* val->intval is signed; the set_*_current_limit() helpers
|
||||
* take a u32. Reject negatives explicitly so a negative
|
||||
* request cannot widen into a huge unsigned value, bypass
|
||||
* the "limit <= cap" bounds check inside the helper, and
|
||||
* silently program the maximum permitted current.
|
||||
*/
|
||||
if (val->intval < 0)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Hold source_lock across the source check and the
|
||||
* resulting hardware write so the IRQ handler cannot
|
||||
* flip ta_in/usb_in between them and have us program the
|
||||
* limit for a source that has just gone offline. Mirror
|
||||
* the DC-priority policy of the get path: if DC is online
|
||||
* route to the DC helper (refuse if DC GPIOs aren't
|
||||
* configured) rather than fall through to USB.
|
||||
*/
|
||||
mutex_lock(&data->source_lock);
|
||||
if (data->ta_in)
|
||||
ret = data->dc_current_limit_gpios ?
|
||||
max8903_set_dc_current_limit(data, val->intval) :
|
||||
-ENODEV;
|
||||
else if (data->usb_in && data->usb_current_limit_gpio)
|
||||
ret = max8903_set_usb_current_limit(data, val->intval);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
mutex_unlock(&data->source_lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max8903_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
struct max8903_data *data = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return data->dc_current_limit_gpios ||
|
||||
data->usb_current_limit_gpio;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t max8903_dcin(int irq, void *_data)
|
||||
{
|
||||
struct max8903_data *data = _data;
|
||||
|
|
@ -91,10 +306,21 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
|
|||
* library as the line should be flagged GPIO_ACTIVE_LOW in the device
|
||||
* tree.
|
||||
*/
|
||||
/*
|
||||
* Hold source_lock across the full read-modify-evaluate block:
|
||||
* - so a concurrent max8903_set_property() sees a consistent
|
||||
* state (lock release would otherwise expose a window where
|
||||
* data->ta_in is updated but the cen/dcm writes still pend);
|
||||
* - so the cen enable calculation reads a stable data->usb_in
|
||||
* rather than racing with max8903_usbin() and writing the
|
||||
* wrong enable state.
|
||||
*/
|
||||
mutex_lock(&data->source_lock);
|
||||
ta_in = gpiod_get_value(data->dok);
|
||||
|
||||
if (ta_in == data->ta_in)
|
||||
if (ta_in == data->ta_in) {
|
||||
mutex_unlock(&data->source_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
data->ta_in = ta_in;
|
||||
|
||||
|
|
@ -119,9 +345,6 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
|
|||
gpiod_set_value(data->cen, val);
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
|
||||
"Connected" : "Disconnected");
|
||||
|
||||
old_type = data->psy_desc.type;
|
||||
|
||||
if (data->ta_in)
|
||||
|
|
@ -130,6 +353,10 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
|
|||
data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
|
||||
else
|
||||
data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
mutex_unlock(&data->source_lock);
|
||||
|
||||
dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
|
||||
"Connected" : "Disconnected");
|
||||
|
||||
if (old_type != data->psy_desc.type)
|
||||
power_supply_changed(data->psy);
|
||||
|
|
@ -150,10 +377,13 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
|
|||
* library as the line should be flagged GPIO_ACTIVE_LOW in the device
|
||||
* tree.
|
||||
*/
|
||||
/* See max8903_dcin(): hold the lock across the full update. */
|
||||
mutex_lock(&data->source_lock);
|
||||
usb_in = gpiod_get_value(data->uok);
|
||||
|
||||
if (usb_in == data->usb_in)
|
||||
if (usb_in == data->usb_in) {
|
||||
mutex_unlock(&data->source_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
data->usb_in = usb_in;
|
||||
|
||||
|
|
@ -176,9 +406,6 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
|
|||
gpiod_set_value(data->cen, val);
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
|
||||
"Connected" : "Disconnected");
|
||||
|
||||
old_type = data->psy_desc.type;
|
||||
|
||||
if (data->ta_in)
|
||||
|
|
@ -187,6 +414,10 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
|
|||
data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
|
||||
else
|
||||
data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
mutex_unlock(&data->source_lock);
|
||||
|
||||
dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
|
||||
"Connected" : "Disconnected");
|
||||
|
||||
if (old_type != data->psy_desc.type)
|
||||
power_supply_changed(data->psy);
|
||||
|
|
@ -221,6 +452,145 @@ static irqreturn_t max8903_fault(int irq, void *_data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max8903_parse_dc_current_limit(struct platform_device *pdev,
|
||||
struct max8903_data *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, i, map_size;
|
||||
u32 *map;
|
||||
|
||||
data->dc_current_limit_gpios = devm_gpiod_get_array_optional(dev,
|
||||
"dc-current-limit", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->dc_current_limit_gpios))
|
||||
return dev_err_probe(dev, PTR_ERR(data->dc_current_limit_gpios),
|
||||
"failed to get DC current limit GPIOs");
|
||||
|
||||
if (!data->dc_current_limit_gpios)
|
||||
return 0; /* Optional feature not present */
|
||||
|
||||
/*
|
||||
* gpio_value entries below are bit patterns indexed into the
|
||||
* dc-current-limit GPIO array. The driver represents them in
|
||||
* a single unsigned long for gpiod_set_array_value_cansleep(),
|
||||
* and BIT(ndescs) further down assumes ndescs fits in a u32
|
||||
* shift; reject pathological DTs at parse time instead of
|
||||
* relying on undefined-behaviour-free dtschema. The binding
|
||||
* already caps maxItems at 4 so this is purely defensive.
|
||||
*/
|
||||
if (data->dc_current_limit_gpios->ndescs >= BITS_PER_TYPE(u32)) {
|
||||
dev_err(dev, "dc-current-limit-gpios: %u GPIOs exceeds %u-bit cap\n",
|
||||
data->dc_current_limit_gpios->ndescs,
|
||||
(unsigned int)BITS_PER_TYPE(u32));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse mapping: pairs of (current_ua, gpio_value) */
|
||||
map_size = device_property_count_u32(dev, "dc-current-limit-mapping");
|
||||
if (map_size <= 0 || map_size % 2) {
|
||||
dev_err(dev, "invalid dc-current-limit-mapping\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* map[] is a scratch buffer used only inside this function to
|
||||
* read the property and unpack it into data->dc_current_limit_map.
|
||||
* Use a plain kmalloc + kfree rather than devm_*: there is no
|
||||
* reason to keep the raw mirror around for the lifetime of the
|
||||
* device.
|
||||
*/
|
||||
map = kmalloc_array(map_size, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u32_array(dev, "dc-current-limit-mapping",
|
||||
map, map_size);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read dc-current-limit-mapping\n");
|
||||
kfree(map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->dc_current_limit_map_size = map_size / 2;
|
||||
data->dc_current_limit_map = devm_kcalloc(dev,
|
||||
data->dc_current_limit_map_size,
|
||||
sizeof(*data->dc_current_limit_map),
|
||||
GFP_KERNEL);
|
||||
if (!data->dc_current_limit_map) {
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->dc_current_limit_map_size; i++) {
|
||||
u32 gpio_value = map[i * 2 + 1];
|
||||
|
||||
/*
|
||||
* gpio_value is the bitmap programmed across the
|
||||
* dc-current-limit GPIOs, so it cannot represent more
|
||||
* bits than the GPIO array width. A larger value would
|
||||
* be silently truncated by gpiod_set_array_value() and
|
||||
* select the wrong limit; reject it at parse time so
|
||||
* the bogus DT is visible to the integrator.
|
||||
*/
|
||||
if (gpio_value >= BIT(data->dc_current_limit_gpios->ndescs)) {
|
||||
dev_err(dev,
|
||||
"dc-current-limit-mapping entry %d: gpio_value 0x%x exceeds %u-GPIO range\n",
|
||||
i, gpio_value,
|
||||
data->dc_current_limit_gpios->ndescs);
|
||||
kfree(map);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->dc_current_limit_map[i].limit_ua = map[i * 2];
|
||||
data->dc_current_limit_map[i].gpio_value = gpio_value;
|
||||
}
|
||||
|
||||
kfree(map);
|
||||
|
||||
/*
|
||||
* devm_gpiod_get_array_optional() above asked for GPIOD_OUT_LOW,
|
||||
* so the hardware mux starts at gpio_value 0. Require the DT
|
||||
* mapping to include a gpio_value=0 entry so the software
|
||||
* current-limit state has a definite initial value matching the
|
||||
* hardware. Without this entry we would have to guess and the
|
||||
* reported INPUT_CURRENT_LIMIT could disagree with what the
|
||||
* mux is actually wired to until a set_property write picks a
|
||||
* real value.
|
||||
*/
|
||||
for (i = 0; i < data->dc_current_limit_map_size; i++)
|
||||
if (data->dc_current_limit_map[i].gpio_value == 0)
|
||||
break;
|
||||
if (i == data->dc_current_limit_map_size) {
|
||||
dev_err(dev,
|
||||
"dc-current-limit-mapping must include a gpio_value=0 entry to describe the boot-time mux state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data->dc_current_limit_ua = data->dc_current_limit_map[i].limit_ua;
|
||||
|
||||
dev_dbg(dev, "DC current limit control: %d levels available, initial %u uA\n",
|
||||
data->dc_current_limit_map_size, data->dc_current_limit_ua);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8903_parse_usb_current_limit(struct platform_device *pdev,
|
||||
struct max8903_data *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
data->usb_current_limit_gpio = devm_gpiod_get_optional(dev,
|
||||
"usb-current-limit", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->usb_current_limit_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(data->usb_current_limit_gpio),
|
||||
"failed to get USB current limit GPIO");
|
||||
|
||||
if (!data->usb_current_limit_gpio)
|
||||
return 0; /* Optional feature not present */
|
||||
|
||||
/* Start at low current (IUSB low = 100 mA) for safety */
|
||||
data->usb_current_limit_ua = MAX8903_USB_CURRENT_LIMIT_LOW_UA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8903_setup_gpios(struct platform_device *pdev)
|
||||
{
|
||||
struct max8903_data *data = platform_get_drvdata(pdev);
|
||||
|
|
@ -335,17 +705,28 @@ static int max8903_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
data->dev = dev;
|
||||
mutex_init(&data->source_lock);
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
ret = max8903_setup_gpios(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max8903_parse_dc_current_limit(pdev, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max8903_parse_usb_current_limit(pdev, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->psy_desc.name = "max8903_charger";
|
||||
data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
|
||||
((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
|
||||
POWER_SUPPLY_TYPE_BATTERY);
|
||||
data->psy_desc.get_property = max8903_get_property;
|
||||
data->psy_desc.set_property = max8903_set_property;
|
||||
data->psy_desc.property_is_writeable = max8903_property_is_writeable;
|
||||
data->psy_desc.properties = max8903_charger_props;
|
||||
data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
|
||||
|
||||
|
|
@ -364,11 +745,8 @@ static int max8903_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"MAX8903 DC IN", data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot request irq %d for DC (%d)\n",
|
||||
gpiod_to_irq(data->dok), ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->uok) {
|
||||
|
|
@ -377,11 +755,8 @@ static int max8903_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"MAX8903 USB IN", data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot request irq %d for USB (%d)\n",
|
||||
gpiod_to_irq(data->uok), ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->flt) {
|
||||
|
|
@ -390,11 +765,8 @@ static int max8903_probe(struct platform_device *pdev)
|
|||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"MAX8903 Fault", data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
|
||||
gpiod_to_irq(data->flt), ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -683,7 +683,7 @@ static int max8971_probe(struct i2c_client *client)
|
|||
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);
|
||||
return err;
|
||||
|
||||
extcon = of_graph_get_remote_node(dev->of_node, -1, -1);
|
||||
if (!extcon)
|
||||
|
|
|
|||
|
|
@ -630,10 +630,8 @@ static int mp2629_charger_probe(struct platform_device *pdev)
|
|||
ret = devm_request_threaded_irq(dev, irq, NULL, mp2629_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
|
||||
"mp2629-charger", charger);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request gpio IRQ\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT,
|
||||
GENMASK(6, 5), BIT(6) | BIT(5));
|
||||
|
|
|
|||
|
|
@ -721,8 +721,7 @@ static int mt6360_chg_irq_register(struct platform_device *pdev)
|
|||
irq_descs[i].name,
|
||||
platform_get_drvdata(pdev));
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n",
|
||||
irq_descs[i].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -837,9 +837,7 @@ static int mt6370_chg_init_irq(struct mt6370_priv *priv)
|
|||
IRQF_TRIGGER_FALLING,
|
||||
dev_name(priv->dev), priv);
|
||||
if (ret)
|
||||
return dev_err_probe(priv->dev, ret,
|
||||
"Failed to request irq %s\n",
|
||||
mt6370_chg_irqs[i].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ static int pf1550_reg_init(struct pf1550_charger *chg)
|
|||
* a battery. The other supported mode is mode 2, the charger is turned
|
||||
* on to charge a battery when present.
|
||||
*/
|
||||
if (power_supply_get_battery_info(chg->charger, &info)) {
|
||||
if (!power_supply_get_battery_info(chg->charger, &info)) {
|
||||
ret = regmap_write(chg->pf1550->regmap,
|
||||
PF1550_CHARG_REG_CHG_OPER,
|
||||
PF1550_CHG_BAT_ON);
|
||||
|
|
@ -612,8 +612,7 @@ static int pf1550_charger_probe(struct platform_device *pdev)
|
|||
IRQF_NO_SUSPEND,
|
||||
"pf1550-charger", chg);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed irq request\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pf1550_dt_parse_dev_info(chg);
|
||||
|
|
|
|||
|
|
@ -231,11 +231,7 @@ static int pm8916_lbc_charger_probe_dt(struct pm8916_lbc_charger *chg)
|
|||
return ret;
|
||||
|
||||
/* Disable charger timeout. */
|
||||
ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, 0x00);
|
||||
}
|
||||
|
||||
static const struct power_supply_desc pm8916_lbc_charger_psy_desc = {
|
||||
|
|
|
|||
|
|
@ -477,6 +477,159 @@ struct power_supply *power_supply_get_by_name(const char *name)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
|
||||
|
||||
static bool power_supply_is_system_battery(struct power_supply *psy)
|
||||
{
|
||||
union power_supply_propval val;
|
||||
|
||||
if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
|
||||
return false;
|
||||
|
||||
if (!power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_SCOPE,
|
||||
&val))
|
||||
if (val.intval == POWER_SUPPLY_SCOPE_DEVICE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __power_supply_get_num_system_batteries(struct power_supply *epsy,
|
||||
void *data)
|
||||
{
|
||||
int *count = data;
|
||||
|
||||
if (power_supply_is_system_battery(epsy))
|
||||
(*count)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int power_supply_get_num_system_batteries(struct device *dev)
|
||||
{
|
||||
int ret, count = 0;
|
||||
|
||||
ret = power_supply_for_each_psy(&count,
|
||||
__power_supply_get_num_system_batteries);
|
||||
|
||||
dev_dbg(dev, "%s: count: %d ret %d\n", __func__, count, ret);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct psy_get_supplies_data {
|
||||
int cnt;
|
||||
int size;
|
||||
struct power_supply **psys;
|
||||
};
|
||||
|
||||
static int
|
||||
__power_supply_populate_system_batteries_array(struct power_supply *epsy,
|
||||
void *_data)
|
||||
{
|
||||
struct psy_get_supplies_data *data = _data;
|
||||
|
||||
if (power_supply_is_system_battery(epsy)) {
|
||||
if (data->size <= data->cnt)
|
||||
return -EOVERFLOW;
|
||||
|
||||
get_device(&epsy->dev);
|
||||
data->psys[data->cnt] = epsy;
|
||||
atomic_inc(&epsy->use_cnt);
|
||||
data->cnt++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
power_supply_populate_system_batteries_array(struct device *dev, int size,
|
||||
struct power_supply **batteries)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct psy_get_supplies_data data = {
|
||||
.cnt = 0,
|
||||
.size = size,
|
||||
.psys = batteries,
|
||||
};
|
||||
|
||||
ret = power_supply_for_each_psy(&data,
|
||||
__power_supply_populate_system_batteries_array);
|
||||
|
||||
dev_dbg(dev, "%s Found %d batteries with array size %d ret %d\n",
|
||||
__func__, data.cnt, data.size, ret);
|
||||
|
||||
if (ret < 0 || !data.cnt) {
|
||||
power_supply_put_system_batteries(batteries, data.cnt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return data.cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* power_supply_get_system_batteries() - Fetches references to battery type
|
||||
* power supplies in the system.
|
||||
* @dev: Pointer to device requesting the power supply refs.
|
||||
* @psys: Pointer to an array of power supply refs.
|
||||
*
|
||||
* Helper function to get handles to battery type power supplies in the system.
|
||||
* If acquiring a ref to a power supply fails, then the search for battery type
|
||||
* power supplies will abort and the acquired power supply references will be
|
||||
* released.
|
||||
*
|
||||
* Return: Indicates the number of battery type power supplies returned on
|
||||
* success or a negative error code on failure.
|
||||
*
|
||||
* Call power_supply_put_system_batteries() after use to cleanup resources.
|
||||
*/
|
||||
int __must_check power_supply_get_system_batteries(struct device *dev,
|
||||
struct power_supply ***psys)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!psys)
|
||||
return -EINVAL;
|
||||
|
||||
ret = power_supply_get_num_system_batteries(dev);
|
||||
if (ret <= 0) {
|
||||
*psys = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*psys = kzalloc_objs(**psys, ret);
|
||||
if (!*psys)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = power_supply_populate_system_batteries_array(dev, ret, *psys);
|
||||
if (ret <= 0)
|
||||
*psys = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_get_system_batteries);
|
||||
|
||||
/**
|
||||
* power_supply_put_system_batteries() - Cleanup resources allocated by
|
||||
* power_supply_get_system_batteries()
|
||||
* @psys: Array of power supply references to release and free.
|
||||
* @count: Number of elements in the array.
|
||||
*/
|
||||
void power_supply_put_system_batteries(struct power_supply **psys, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (psys[i])
|
||||
power_supply_put(psys[i]);
|
||||
}
|
||||
|
||||
kfree(psys);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_put_system_batteries);
|
||||
|
||||
/**
|
||||
* power_supply_put() - Drop reference obtained with power_supply_get_by_name
|
||||
* @psy: Reference to put
|
||||
|
|
@ -1624,6 +1777,14 @@ __power_supply_register(struct device *parent,
|
|||
init_rwsem(&psy->extensions_sem);
|
||||
INIT_LIST_HEAD(&psy->extensions);
|
||||
|
||||
if (desc->init) {
|
||||
rc = desc->init(psy);
|
||||
if (WARN_ON_ONCE(rc > 0))
|
||||
rc = -EINVAL;
|
||||
if (rc)
|
||||
goto check_supplies_failed;
|
||||
}
|
||||
|
||||
rc = device_add(dev);
|
||||
if (rc)
|
||||
goto device_add_failed;
|
||||
|
|
@ -1636,11 +1797,11 @@ __power_supply_register(struct device *parent,
|
|||
if (rc)
|
||||
goto register_thermal_failed;
|
||||
|
||||
rc = power_supply_create_triggers(psy);
|
||||
if (rc)
|
||||
goto create_triggers_failed;
|
||||
|
||||
scoped_guard(rwsem_read, &psy->extensions_sem) {
|
||||
rc = power_supply_create_triggers(psy);
|
||||
if (rc)
|
||||
goto create_triggers_failed;
|
||||
|
||||
rc = power_supply_add_hwmon_sysfs(psy);
|
||||
if (rc)
|
||||
goto add_hwmon_sysfs_failed;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include "power_supply.h"
|
||||
|
||||
/* Battery specific LEDs triggers. */
|
||||
|
||||
struct power_supply_led_trigger {
|
||||
struct led_trigger trig;
|
||||
struct power_supply *psy;
|
||||
|
|
@ -89,7 +87,7 @@ static void power_supply_unregister_led_trigger(struct led_trigger *trig)
|
|||
kfree(psy_trig);
|
||||
}
|
||||
|
||||
static void power_supply_update_bat_leds(struct power_supply *psy)
|
||||
static void power_supply_update_status_leds(struct power_supply *psy)
|
||||
{
|
||||
union power_supply_propval status;
|
||||
unsigned int intensity_green[3] = { 0, 255, 0 };
|
||||
|
|
@ -102,7 +100,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
|
|||
|
||||
switch (status.intval) {
|
||||
case POWER_SUPPLY_STATUS_FULL:
|
||||
led_trigger_event(psy->trig, LED_FULL);
|
||||
led_trigger_event(psy->charging_or_full_trig, LED_FULL);
|
||||
led_trigger_event(psy->charging_trig, LED_OFF);
|
||||
led_trigger_event(psy->full_trig, LED_FULL);
|
||||
/* Going from blink to LED on requires a LED_OFF event to stop blink */
|
||||
|
|
@ -114,7 +112,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
|
|||
LED_FULL);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_CHARGING:
|
||||
led_trigger_event(psy->trig, LED_FULL);
|
||||
led_trigger_event(psy->charging_or_full_trig, LED_FULL);
|
||||
led_trigger_event(psy->charging_trig, LED_FULL);
|
||||
led_trigger_event(psy->full_trig, LED_OFF);
|
||||
led_trigger_blink(psy->charging_blink_full_solid_trig, 0, 0);
|
||||
|
|
@ -124,7 +122,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
|
|||
LED_FULL);
|
||||
break;
|
||||
default:
|
||||
led_trigger_event(psy->trig, LED_OFF);
|
||||
led_trigger_event(psy->charging_or_full_trig, LED_OFF);
|
||||
led_trigger_event(psy->charging_trig, LED_OFF);
|
||||
led_trigger_event(psy->full_trig, LED_OFF);
|
||||
led_trigger_event(psy->charging_blink_full_solid_trig,
|
||||
|
|
@ -135,21 +133,15 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
|
|||
}
|
||||
}
|
||||
|
||||
static void power_supply_remove_bat_triggers(struct power_supply *psy)
|
||||
{
|
||||
power_supply_unregister_led_trigger(psy->trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_trig);
|
||||
power_supply_unregister_led_trigger(psy->full_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_blink_full_solid_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_orange_full_green_trig);
|
||||
}
|
||||
|
||||
static int power_supply_create_bat_triggers(struct power_supply *psy)
|
||||
static int power_supply_create_status_triggers(struct power_supply *psy)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!power_supply_has_property(psy, POWER_SUPPLY_PROP_STATUS))
|
||||
return 0;
|
||||
|
||||
power_supply_register_led_trigger(psy, "%s-charging-or-full",
|
||||
&psy->trig, &err);
|
||||
&psy->charging_or_full_trig, &err);
|
||||
power_supply_register_led_trigger(psy, "%s-charging",
|
||||
&psy->charging_trig, &err);
|
||||
power_supply_register_led_trigger(psy, "%s-full",
|
||||
|
|
@ -158,15 +150,11 @@ static int power_supply_create_bat_triggers(struct power_supply *psy)
|
|||
&psy->charging_blink_full_solid_trig, &err);
|
||||
power_supply_register_led_trigger(psy, "%s-charging-orange-full-green",
|
||||
&psy->charging_orange_full_green_trig, &err);
|
||||
if (err)
|
||||
power_supply_remove_bat_triggers(psy);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Generated power specific LEDs triggers. */
|
||||
|
||||
static void power_supply_update_gen_leds(struct power_supply *psy)
|
||||
static void power_supply_update_online_leds(struct power_supply *psy)
|
||||
{
|
||||
union power_supply_propval online;
|
||||
|
||||
|
|
@ -175,43 +163,53 @@ static void power_supply_update_gen_leds(struct power_supply *psy)
|
|||
|
||||
dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
|
||||
|
||||
if (online.intval)
|
||||
led_trigger_event(psy->trig, LED_FULL);
|
||||
else
|
||||
led_trigger_event(psy->trig, LED_OFF);
|
||||
led_trigger_event(psy->online_trig, online.intval ? LED_FULL : LED_OFF);
|
||||
}
|
||||
|
||||
static int power_supply_create_gen_triggers(struct power_supply *psy)
|
||||
static int power_supply_create_online_trigger(struct power_supply *psy)
|
||||
{
|
||||
return power_supply_register_led_trigger(psy, "%s-online", &psy->trig, NULL);
|
||||
}
|
||||
int err = 0;
|
||||
|
||||
static void power_supply_remove_gen_triggers(struct power_supply *psy)
|
||||
{
|
||||
power_supply_unregister_led_trigger(psy->trig);
|
||||
}
|
||||
if (!power_supply_has_property(psy, POWER_SUPPLY_PROP_ONLINE))
|
||||
return 0;
|
||||
|
||||
/* Choice what triggers to create&update. */
|
||||
power_supply_register_led_trigger(psy, "%s-online", &psy->online_trig,
|
||||
&err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void power_supply_update_leds(struct power_supply *psy)
|
||||
{
|
||||
if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
|
||||
power_supply_update_bat_leds(psy);
|
||||
else
|
||||
power_supply_update_gen_leds(psy);
|
||||
power_supply_update_online_leds(psy);
|
||||
power_supply_update_status_leds(psy);
|
||||
}
|
||||
|
||||
int power_supply_create_triggers(struct power_supply *psy)
|
||||
{
|
||||
if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
|
||||
return power_supply_create_bat_triggers(psy);
|
||||
return power_supply_create_gen_triggers(psy);
|
||||
int err;
|
||||
|
||||
err = power_supply_create_online_trigger(psy);
|
||||
if (err)
|
||||
goto err_remove;
|
||||
|
||||
err = power_supply_create_status_triggers(psy);
|
||||
if (err)
|
||||
goto err_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove:
|
||||
power_supply_remove_triggers(psy);
|
||||
return err;
|
||||
}
|
||||
|
||||
void power_supply_remove_triggers(struct power_supply *psy)
|
||||
{
|
||||
if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
|
||||
power_supply_remove_bat_triggers(psy);
|
||||
else
|
||||
power_supply_remove_gen_triggers(psy);
|
||||
power_supply_unregister_led_trigger(psy->online_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_or_full_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_trig);
|
||||
power_supply_unregister_led_trigger(psy->full_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_blink_full_solid_trig);
|
||||
power_supply_unregister_led_trigger(psy->charging_orange_full_green_trig);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {
|
|||
[POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe",
|
||||
[POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd",
|
||||
[POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn",
|
||||
[POWER_SUPPLY_TECHNOLOGY_PbAc] = "PbAc",
|
||||
[POWER_SUPPLY_TECHNOLOGY_NiZn] = "NiZn",
|
||||
[POWER_SUPPLY_TECHNOLOGY_RAM] = "RAM",
|
||||
[POWER_SUPPLY_TECHNOLOGY_ZnAr] = "ZnAr",
|
||||
};
|
||||
|
||||
static const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
|
|
@ -1231,17 +1232,17 @@ static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src)
|
|||
memcpy(dest, src + 1, len);
|
||||
dest[len] = '\0';
|
||||
} else {
|
||||
memcpy(dest, src, BATTMGR_STRING_LEN);
|
||||
strscpy(dest, src, BATTMGR_STRING_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int qcom_battmgr_sc8280xp_parse_technology(const char *chemistry)
|
||||
{
|
||||
if ((!strncmp(chemistry, "LIO", BATTMGR_CHEMISTRY_LEN)) ||
|
||||
(!strncmp(chemistry, "OOI", BATTMGR_CHEMISTRY_LEN)))
|
||||
if ((!strncmp(chemistry, "LIO", 3)) ||
|
||||
(!strncmp(chemistry, "OOI", 3)))
|
||||
return POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
if (!strncmp(chemistry, "LIP", BATTMGR_CHEMISTRY_LEN) ||
|
||||
!strncmp(chemistry, "LiP", BATTMGR_CHEMISTRY_LEN))
|
||||
if (!strncmp(chemistry, "LIP", 3) ||
|
||||
!strncmp(chemistry, "LiP", 3))
|
||||
return POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
|
||||
pr_err("Unknown battery technology '%s'\n", chemistry);
|
||||
|
|
@ -1648,7 +1649,6 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
|
|||
psy_cfg_supply.supplied_to = qcom_battmgr_battery;
|
||||
psy_cfg_supply.num_supplicants = 1;
|
||||
|
||||
INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker);
|
||||
mutex_init(&battmgr->lock);
|
||||
init_completion(&battmgr->ack);
|
||||
|
||||
|
|
@ -1711,6 +1711,11 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
|
|||
"failed to register wireless charing power supply\n");
|
||||
}
|
||||
|
||||
ret = devm_work_autocancel(dev, &battmgr->enable_work,
|
||||
qcom_battmgr_enable_worker);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
battmgr->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_BATTMGR,
|
||||
qcom_battmgr_callback,
|
||||
qcom_battmgr_pdr_notify,
|
||||
|
|
|
|||
|
|
@ -937,11 +937,8 @@ static int smbb_charger_probe(struct platform_device *pdev)
|
|||
rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
smbb_charger_irqs[i].handler, IRQF_ONESHOT,
|
||||
smbb_charger_irqs[i].name, chg);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "failed to request irq '%s'\n",
|
||||
smbb_charger_irqs[i].name);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -922,8 +922,7 @@ static int smb_init_irq(struct smb_chip *chip, int *irq, const char *name,
|
|||
rc = devm_request_threaded_irq(chip->dev, irqnum, NULL, handler,
|
||||
IRQF_ONESHOT, name, chip);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(chip->dev, rc, "Couldn't request irq %s\n",
|
||||
name);
|
||||
return rc;
|
||||
|
||||
if (irq)
|
||||
*irq = irqnum;
|
||||
|
|
|
|||
|
|
@ -1186,19 +1186,15 @@ static int rk817_charger_probe(struct platform_device *pdev)
|
|||
rk817_plug_in_isr,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"rk817_plug_in", charger);
|
||||
if (ret) {
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"plug_in_irq request failed!\n");
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(charger->dev, plugout_irq, NULL,
|
||||
rk817_plug_out_isr,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"rk817_plug_out", charger);
|
||||
if (ret) {
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"plug_out_irq request failed!\n");
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &charger->work,
|
||||
rk817_charging_monitor);
|
||||
|
|
|
|||
|
|
@ -800,11 +800,8 @@ static int rn5t618_power_probe(struct platform_device *pdev)
|
|||
"rn5t618_power",
|
||||
&pdev->dev);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request IRQ:%d fail\n",
|
||||
info->irq);
|
||||
if (ret < 0)
|
||||
info->irq = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1582,6 +1582,19 @@ static const struct regmap_config rt9455_regmap_config = {
|
|||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static void rt9455_cancel_all_delayed_works(void *data)
|
||||
{
|
||||
struct rt9455_info *info = data;
|
||||
|
||||
/*
|
||||
* Both pwr_rdy_work and batt_presence_work can queue
|
||||
* max_charging_time_work, so cancel them first.
|
||||
*/
|
||||
cancel_delayed_work_sync(&info->pwr_rdy_work);
|
||||
cancel_delayed_work_sync(&info->batt_presence_work);
|
||||
cancel_delayed_work_sync(&info->max_charging_time_work);
|
||||
}
|
||||
|
||||
static int rt9455_probe(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
|
|
@ -1672,14 +1685,16 @@ static int rt9455_probe(struct i2c_client *client)
|
|||
goto put_usb_notifier;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, rt9455_cancel_all_delayed_works, info);
|
||||
if (ret)
|
||||
goto put_usb_notifier;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
rt9455_irq_handler_thread,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
RT9455_DRIVER_NAME, info);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IRQ handler\n");
|
||||
if (ret)
|
||||
goto put_usb_notifier;
|
||||
}
|
||||
|
||||
ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
|
||||
if (ret) {
|
||||
|
|
@ -1712,10 +1727,6 @@ static void rt9455_remove(struct i2c_client *client)
|
|||
if (info->nb.notifier_call)
|
||||
usb_unregister_notifier(info->usb_phy, &info->nb);
|
||||
#endif
|
||||
|
||||
cancel_delayed_work_sync(&info->pwr_rdy_work);
|
||||
cancel_delayed_work_sync(&info->max_charging_time_work);
|
||||
cancel_delayed_work_sync(&info->batt_presence_work);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id rt9455_i2c_id_table[] = {
|
||||
|
|
|
|||
|
|
@ -1030,8 +1030,7 @@ static int rt9467_request_interrupt(struct rt9467_chg_data *data)
|
|||
ret = devm_request_threaded_irq(dev, virq, NULL, chg_irqs[i].handler,
|
||||
IRQF_ONESHOT, chg_irqs[i].name, data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request (%s) irq\n",
|
||||
chg_irqs[i].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -369,23 +369,28 @@ static int rt9471_charger_set_property(struct power_supply *psy,
|
|||
const union power_supply_propval *val)
|
||||
{
|
||||
struct rt9471_chip *chip = power_supply_get_drvdata(psy);
|
||||
int value = val->intval;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
return regmap_field_write(chip->rm_fields[F_CHG_EN], !!value);
|
||||
return regmap_field_write(chip->rm_fields[F_CHG_EN],
|
||||
!!val->intval);
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return regmap_field_write(chip->rm_fields[F_HZ], !value);
|
||||
return regmap_field_write(chip->rm_fields[F_HZ], !val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return rt9471_set_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, value);
|
||||
return rt9471_set_value_by_field_range(
|
||||
chip, F_ICHG_REG, RT9471_RANGE_ICHG, val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
return rt9471_set_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, value);
|
||||
return rt9471_set_value_by_field_range(
|
||||
chip, F_VBAT_REG, RT9471_RANGE_VCHG, val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return rt9471_set_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, value);
|
||||
return rt9471_set_value_by_field_range(
|
||||
chip, F_AICR, RT9471_RANGE_AICR, val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
return rt9471_set_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, value);
|
||||
return rt9471_set_value_by_field_range(
|
||||
chip, F_MIVR, RT9471_RANGE_MIVR, val->intval);
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
return rt9471_set_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, value);
|
||||
return rt9471_set_value_by_field_range(
|
||||
chip, F_IPRE_CHG, RT9471_RANGE_IPRE, val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return rt9471_set_ieoc(chip, val->intval);
|
||||
default:
|
||||
|
|
@ -401,35 +406,39 @@ static int rt9471_charger_get_property(struct power_supply *psy,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
struct rt9471_chip *chip = power_supply_get_drvdata(psy);
|
||||
int *pvalue = &val->intval;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
return rt9471_get_status(chip, pvalue);
|
||||
return rt9471_get_status(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return rt9471_get_vbus_good(chip, pvalue);
|
||||
return rt9471_get_vbus_good(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
return rt9471_get_usb_type_current(chip, pvalue);
|
||||
return rt9471_get_usb_type_current(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return rt9471_get_value_by_field_range(chip, F_ICHG_REG, RT9471_RANGE_ICHG, pvalue);
|
||||
return rt9471_get_value_by_field_range(
|
||||
chip, F_ICHG_REG, RT9471_RANGE_ICHG, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
*pvalue = RT9471_ICHG_MAXUA;
|
||||
val->intval = RT9471_ICHG_MAXUA;
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
return rt9471_get_value_by_field_range(chip, F_VBAT_REG, RT9471_RANGE_VCHG, pvalue);
|
||||
return rt9471_get_value_by_field_range(
|
||||
chip, F_VBAT_REG, RT9471_RANGE_VCHG, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
|
||||
val->intval = RT9471_VCHG_MAXUV;
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return rt9471_get_value_by_field_range(chip, F_AICR, RT9471_RANGE_AICR, pvalue);
|
||||
return rt9471_get_value_by_field_range(
|
||||
chip, F_AICR, RT9471_RANGE_AICR, &val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
return rt9471_get_value_by_field_range(chip, F_MIVR, RT9471_RANGE_MIVR, pvalue);
|
||||
return rt9471_get_value_by_field_range(
|
||||
chip, F_MIVR, RT9471_RANGE_MIVR, &val->intval);
|
||||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
return rt9471_get_usb_type(chip, pvalue);
|
||||
return rt9471_get_usb_type(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
return rt9471_get_value_by_field_range(chip, F_IPRE_CHG, RT9471_RANGE_IPRE, pvalue);
|
||||
return rt9471_get_value_by_field_range(
|
||||
chip, F_IPRE_CHG, RT9471_RANGE_IPRE, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return rt9471_get_ieoc(chip, pvalue);
|
||||
return rt9471_get_ieoc(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = rt9471_model;
|
||||
return 0;
|
||||
|
|
@ -583,8 +592,7 @@ static int rt9471_register_interrupts(struct rt9471_chip *chip)
|
|||
ret = devm_request_threaded_irq(dev, virq, NULL, curr->handler,
|
||||
IRQF_ONESHOT, curr->name, chip);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register IRQ (%s)\n",
|
||||
curr->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ struct sbs_info {
|
|||
u32 flags;
|
||||
int technology;
|
||||
char strings[NR_STRING_BUFFERS][I2C_SMBUS_BLOCK_MAX + 1];
|
||||
char serial[5];
|
||||
};
|
||||
|
||||
static char *sbs_get_string_buf(struct sbs_info *chip,
|
||||
|
|
@ -821,18 +822,18 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static char sbs_serial[5];
|
||||
static int sbs_get_battery_serial_number(struct i2c_client *client,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct sbs_info *chip = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sprintf(sbs_serial, "%04x", ret);
|
||||
val->strval = sbs_serial;
|
||||
sprintf(chip->serial, "%04x", (u16)ret);
|
||||
val->strval = chip->serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -860,6 +861,14 @@ static int sbs_get_chemistry(struct sbs_info *chip,
|
|||
chip->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
|
||||
else if (!strncasecmp(chemistry, "NiMH", 4))
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
|
||||
else if (!strncasecmp(chemistry, "PbAc", 4))
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_PbAc;
|
||||
else if (!strncasecmp(chemistry, "NiZn", 4))
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_NiZn;
|
||||
else if (!strncasecmp(chemistry, "RAM", 3))
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_RAM;
|
||||
else if (!strncasecmp(chemistry, "ZnAr", 4))
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_ZnAr;
|
||||
else
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ static int sbs_probe(struct i2c_client *client)
|
|||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
dev_name(&client->dev), chip);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to request irq\n");
|
||||
return ret;
|
||||
} else {
|
||||
ret = devm_delayed_work_autocancel(&client->dev, &chip->work,
|
||||
sbs_delayed_work);
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ static int sc2731_charger_probe(struct platform_device *pdev)
|
|||
mutex_init(&info->lock);
|
||||
info->dev = &pdev->dev;
|
||||
INIT_WORK(&info->work, sc2731_charger_work);
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!info->regmap) {
|
||||
|
|
@ -516,6 +517,7 @@ static void sc2731_charger_remove(struct platform_device *pdev)
|
|||
struct sc2731_charger_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
usb_unregister_notifier(info->usb_phy, &info->usb_notify);
|
||||
cancel_work_sync(&info->work);
|
||||
}
|
||||
|
||||
static const struct of_device_id sc2731_charger_of_match[] = {
|
||||
|
|
|
|||
|
|
@ -1231,10 +1231,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
|
|||
sc27xx_fgu_interrupt,
|
||||
IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
pdev->name, data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "failed to request fgu IRQ\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq = gpiod_to_irq(data->gpiod);
|
||||
if (irq < 0) {
|
||||
|
|
@ -1247,10 +1245,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
|
|||
IRQF_ONESHOT | IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
pdev->name, data);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
1127
drivers/power/supply/sgm41542_charger.c
Normal file
1127
drivers/power/supply/sgm41542_charger.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -337,7 +337,7 @@ static int srt_ec_probe(struct i2c_client *client)
|
|||
ret = devm_request_threaded_irq(dev, client->irq, NULL, srt_psy_detect_irq,
|
||||
IRQF_ONESHOT, client->name, srt);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to request interrupt\n");
|
||||
return ret;
|
||||
|
||||
ret = devm_delayed_work_autocancel(dev, &srt->poll_work, srt_bat_poll_work);
|
||||
if (ret < 0)
|
||||
|
|
|
|||
|
|
@ -302,12 +302,8 @@ static int tps65090_charger_probe(struct platform_device *pdev)
|
|||
if (irq != -ENXIO) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
tps65090_charger_isr, IRQF_ONESHOT, "tps65090-charger", cdata);
|
||||
if (ret) {
|
||||
dev_err(cdata->dev,
|
||||
"Unable to register irq %d err %d\n", irq,
|
||||
ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
cdata->poll_task = kthread_run(tps65090_charger_poll_task,
|
||||
cdata, "ktps65090charger");
|
||||
|
|
|
|||
|
|
@ -239,12 +239,8 @@ static int tps65217_charger_probe(struct platform_device *pdev)
|
|||
tps65217_charger_irq,
|
||||
IRQF_SHARED, "tps65217-charger",
|
||||
charger);
|
||||
if (ret) {
|
||||
dev_err(charger->dev,
|
||||
"Unable to register irq %d err %d\n", irq[i],
|
||||
ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check current state */
|
||||
tps65217_charger_irq(-1, charger);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/twl.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
|
@ -1002,8 +1003,15 @@ static int twl4030_bci_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, bci);
|
||||
|
||||
INIT_WORK(&bci->work, twl4030_bci_usb_work);
|
||||
INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &bci->current_worker,
|
||||
twl4030_current_worker);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_work_autocancel(&pdev->dev, &bci->work,
|
||||
twl4030_bci_usb_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
|
||||
if (IS_ERR(bci->channel_vac)) {
|
||||
|
|
@ -1055,19 +1063,13 @@ static int twl4030_bci_probe(struct platform_device *pdev)
|
|||
ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
|
||||
twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
|
||||
bci);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not request irq %d, status %d\n",
|
||||
bci->irq_chg, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
|
||||
twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not request irq %d, status %d\n",
|
||||
bci->irq_bci, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable interrupts now. */
|
||||
reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
|
||||
|
|
|
|||
|
|
@ -542,9 +542,7 @@ static int twl6030_charger_probe(struct platform_device *pdev)
|
|||
IRQF_ONESHOT, pdev->name,
|
||||
charger);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"could not request irq %d\n",
|
||||
charger->irq_chg);
|
||||
return ret;
|
||||
|
||||
/* turing to charging to configure things */
|
||||
twl6030_charger_write(CONTROLLER_CTRL1, 0);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
|
@ -640,28 +641,25 @@ static int ucs1002_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
info->health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll);
|
||||
ret = devm_delayed_work_autocancel(dev, &info->health_poll,
|
||||
ucs1002_health_poll);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (irq_a_det > 0) {
|
||||
ret = devm_request_threaded_irq(dev, irq_a_det, NULL,
|
||||
ucs1002_charger_irq,
|
||||
IRQF_ONESHOT,
|
||||
"ucs1002-a_det", info);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request A_DET threaded irq: %d\n",
|
||||
ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq_alert > 0) {
|
||||
ret = devm_request_irq(dev, irq_alert, ucs1002_alert_irq,
|
||||
0,"ucs1002-alert", info);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request ALERT threaded irq: %d\n",
|
||||
ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,20 @@
|
|||
#define BQ25792_REG0A_TRECHG_MASK GENMASK(5, 4)
|
||||
#define BQ25792_REG0A_VRECHG_MASK GENMASK(3, 0)
|
||||
|
||||
#define BQ25792_CELL_1S 0
|
||||
#define BQ25792_CELL_2S 1
|
||||
#define BQ25792_CELL_3S 2
|
||||
#define BQ25792_CELL_4S 3
|
||||
|
||||
#define BQ25792_TRECHG_64MS 0
|
||||
#define BQ25792_TRECHG_256MS 1
|
||||
#define BQ25792_TRECHG_1024MS 2
|
||||
#define BQ25792_TRECHG_2048MS 3
|
||||
|
||||
#define BQ25792_VRECHG_MIN_UV 50000
|
||||
#define BQ25792_VRECHG_STEP_UV 50000
|
||||
#define BQ25792_VRECHG_MAX_UV 800000
|
||||
|
||||
/* VOTG regulation */
|
||||
#define BQ25792_REG0B_VOTG_MASK GENMASK(10, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
|
||||
|
||||
#define MAX17055_MODELCFG_REFRESH_BIT BIT(15)
|
||||
#define MAX17055_MODELCFG_VCHG_BIT BIT(10)
|
||||
|
||||
enum max17042_register {
|
||||
MAX17042_STATUS = 0x00,
|
||||
|
|
@ -124,6 +125,7 @@ enum max17055_register {
|
|||
|
||||
MAX17055_ConvgCfg = 0x49,
|
||||
MAX17055_VFRemCap = 0x4A,
|
||||
MAX17055_SoftWakeup = 0x60,
|
||||
|
||||
MAX17055_STATUS2 = 0xB0,
|
||||
MAX17055_POWER = 0xB1,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ enum {
|
|||
POWER_SUPPLY_TECHNOLOGY_LiFe,
|
||||
POWER_SUPPLY_TECHNOLOGY_NiCd,
|
||||
POWER_SUPPLY_TECHNOLOGY_LiMn,
|
||||
POWER_SUPPLY_TECHNOLOGY_PbAc,
|
||||
POWER_SUPPLY_TECHNOLOGY_NiZn,
|
||||
POWER_SUPPLY_TECHNOLOGY_RAM,
|
||||
POWER_SUPPLY_TECHNOLOGY_ZnAr,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -281,6 +285,15 @@ struct power_supply_desc {
|
|||
int (*property_is_writeable)(struct power_supply *psy,
|
||||
enum power_supply_property psp);
|
||||
void (*external_power_changed)(struct power_supply *psy);
|
||||
/*
|
||||
* Optional registration-time initialization. This runs in sleepable
|
||||
* process context after driver data and any battery info are available,
|
||||
* but before the device is added. The callback must not publish changes or
|
||||
* start asynchronous activity that can access the power supply before
|
||||
* registration completes. Return 0 on success or a negative errno on
|
||||
* failure.
|
||||
*/
|
||||
int (*init)(struct power_supply *psy);
|
||||
|
||||
/*
|
||||
* Set if thermal zone should not be created for this power supply.
|
||||
|
|
@ -346,7 +359,8 @@ struct power_supply {
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
struct led_trigger *trig;
|
||||
struct led_trigger *charging_or_full_trig;
|
||||
struct led_trigger *online_trig;
|
||||
struct led_trigger *charging_trig;
|
||||
struct led_trigger *full_trig;
|
||||
struct led_trigger *charging_blink_full_solid_trig;
|
||||
|
|
@ -802,15 +816,33 @@ struct power_supply_battery_info {
|
|||
int bti_resistance_tolerance;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
|
||||
extern int power_supply_reg_notifier(struct notifier_block *nb);
|
||||
extern void power_supply_unreg_notifier(struct notifier_block *nb);
|
||||
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
|
||||
extern struct power_supply *power_supply_get_by_name(const char *name);
|
||||
extern int __must_check power_supply_get_system_batteries(struct device *dev,
|
||||
struct power_supply ***psys);
|
||||
extern void power_supply_put_system_batteries(struct power_supply **psys, int count);
|
||||
extern void power_supply_put(struct power_supply *psy);
|
||||
#else
|
||||
static inline int power_supply_reg_notifier(struct notifier_block *nb)
|
||||
{ return -EOPNOTSUPP; }
|
||||
static inline void power_supply_unreg_notifier(struct notifier_block *nb) {}
|
||||
static inline void power_supply_put(struct power_supply *psy) {}
|
||||
static inline struct power_supply *power_supply_get_by_name(const char *name)
|
||||
{ return NULL; }
|
||||
static inline int __must_check power_supply_get_system_batteries(struct device *dev,
|
||||
struct power_supply ***psys)
|
||||
{
|
||||
if (psys)
|
||||
*psys = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void power_supply_put_system_batteries(struct power_supply **psys,
|
||||
int count)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *fwnode,
|
||||
const char *property);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ test_sysfs_prop_optional_range() {
|
|||
|
||||
test_sysfs_prop_optional_list() {
|
||||
PROP=$1
|
||||
LIST=$2
|
||||
shift
|
||||
|
||||
TEST_NAME="$DEVNAME".sysfs."$PROP"
|
||||
|
||||
|
|
@ -118,9 +118,9 @@ test_sysfs_prop_optional_list() {
|
|||
valid=0
|
||||
|
||||
OLDIFS=$IFS
|
||||
IFS=","
|
||||
for item in $LIST; do
|
||||
if [ "$DATA" = "$item" ]; then
|
||||
IFS=",$IFS"
|
||||
for item in $*; do
|
||||
if [ "$item" ] && [ "$DATA" = "$item" ]; then
|
||||
valid=1
|
||||
break
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ for DEVNAME in $supplies; do
|
|||
test_sysfs_prop_optional model_name
|
||||
test_sysfs_prop_optional manufacturer
|
||||
test_sysfs_prop_optional serial_number
|
||||
test_sysfs_prop_optional_list technology "Unknown","NiMH","Li-ion","Li-poly","LiFe","NiCd","LiMn"
|
||||
test_sysfs_prop_optional_list technology "Unknown","NiMH","Li-ion","Li-poly","LiFe","NiCd"\
|
||||
,"LiMn","PbAc","NiZn","RAM","ZnAr"
|
||||
|
||||
test_sysfs_prop_optional cycle_count
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user