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:
Linus Torvalds 2026-08-21 12:05:10 -07:00
commit 2be02a7c99
86 changed files with 4448 additions and 446 deletions

View File

@ -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,

View File

@ -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

View File

@ -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:
- |

View File

@ -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>;
};

View File

@ -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>;
};
};
};
};

View File

@ -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>;
};
};
...

View File

@ -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

View File

@ -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

View File

@ -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

View 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");

View File

@ -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",

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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))

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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),

View File

@ -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)

View File

@ -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 */
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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, &regval, 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, &regval, 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, &reg);
if (ret)
return ret;
pdata->online = reg & BQ25792_REG1B_PG_STAT;
ret = regmap_read(pdata->bq->regmap, BQ25792_REG1C_CHARGER_STATUS_1, &reg);
if (ret)
return ret;
pdata->charging = reg & BQ25792_REG1C_CHG_STAT_MASK;
ret = regmap_read(pdata->bq->regmap, BQ25792_REG20_FAULT_STATUS_0, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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);

View File

@ -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 */

View File

@ -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,

View File

@ -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[] = {

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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" },

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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[] = {

View File

@ -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 = {

View File

@ -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");

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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 = {

View File

@ -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;

View File

@ -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);
}

View File

@ -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[] = {

View File

@ -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,

View File

@ -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;
}
}
/*

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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[] = {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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[] = {

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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");

View File

@ -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);

View File

@ -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 |

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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