Merge tag 'for-v7.2' 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 drivers:

   - New EC driver providing battery info for Microsoft Surface RT

   - New driver for battery charger in Samsung S2M PMICs

   - Rework max17042 driver

   - sysfs control for bd71828 auto input current limitation

  All over:

   - Use named fields for struct platform_device_id and of_device_id
     entries

   - Misc small cleanups and fixes"

* tag 'for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (33 commits)
  Documentation: ABI: sysfs-class-reboot-mode-reboot_modes: fix doc warnings
  power: supply: charger-manager: fix refcount leak in is_full_charged()
  power: supply: core: fix supplied_from allocations
  power: supply: max17042_battery: Use modern PM ops to clear up warning
  power: supply: add support for Samsung S2M series PMIC charger device
  power: supply: Add support for Surface RT battery and charger
  dt-bindings: embedded-controller: Document Surface RT EC
  power: supply: bd71828: sysfs for auto input current limitation
  power: supply: cpcap-charger: include missing <linux/property.h>
  power: supply: cros_charge-control: Move MODULE_DEVICE_TABLE next to the table itself
  power: supply: ab8500_fg: Fix typos in comments
  power: supply: Use named initializers for arrays of i2c_device_data
  power: supply: Remove unused jz4740-battery.h
  power: reset: st-poweroff: Use of_device_get_match_data()
  power: supply: bq257xx: Add fields for 'charging' and 'overvoltage' states
  power: supply: bq257xx: Consistently use indirect get/set helpers
  power: supply: bq257xx: Make the default current limit a per-chip attribute
  power: supply: bq257xx: Fix VSYSMIN clamping logic
  power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak
  power: supply: max17042: fix OF node reference imbalance
  ...
This commit is contained in:
Linus Torvalds
2026-06-19 18:13:09 -07:00
64 changed files with 1323 additions and 416 deletions

View File

@@ -0,0 +1,12 @@
What: /sys/class/power_supply/bd71828_ac/auto_dcin_limit
Description:
Enable/Disable automatic management of input current limit
(ILIM_DCIN_EN bit).
Possible values are:
============ ===========================================
1 automatic adjustment of input current limit
0 no adjustment of input current limit. This
helps for more unusual power sources like
solar modules.

View File

@@ -2,33 +2,36 @@ What: /sys/class/reboot-mode/<driver>/reboot_modes
Date: March 2026(TBD)
KernelVersion: TBD
Contact: linux-pm@vger.kernel.org
Description:
Description:
This interface exposes the reboot-mode arguments
registered with the reboot-mode framework. It is
a read-only interface and provides a space
separated list of reboot-mode arguments supported
on the current platform.
Example:
recovery fastboot bootloader
The exact sysfs path may vary depending on the
name of the driver that registers the arguments.
Example:
Example::
/sys/class/reboot-mode/nvmem-reboot-mode/reboot_modes
/sys/class/reboot-mode/syscon-reboot-mode/reboot_modes
/sys/class/reboot-mode/qcom-pon/reboot_modes
The supported arguments can be used by userspace to
invoke device reset using the standard reboot() system
call interface, with the "argument" as string to "*arg"
parameter along with LINUX_REBOOT_CMD_RESTART2.
call interface, with the "argument" as string to ``*arg``
parameter along with ``LINUX_REBOOT_CMD_RESTART2``.
A driver can expose the supported arguments by
registering them with the reboot-mode framework
using the property names that follow the
mode-<argument> format.
Example:
mode-bootloader, mode-recovery.
mode-bootloader, mode-recovery
This attribute is useful for scripts or initramfs
logic that need to programmatically determine

View File

@@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/embedded-controller/microsoft,surface-rt-ec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microsoft Surface RT fuel gauge and charger EC
maintainers:
- Jonas Schwöbel <jonasschwoebel@yahoo.de>
- Svyatoslav Ryhel <clamor95@gmail.com>
description:
An Embedded Controller used in Microsoft Surface RT for monitoring
battery properties and charger status.
allOf:
- $ref: /schemas/power/supply/power-supply.yaml#
properties:
compatible:
const: microsoft,surface-rt-ec
reg:
maxItems: 1
interrupts:
maxItems: 1
enable-gpios:
maxItems: 1
monitored-battery: true
required:
- compatible
- reg
- interrupts
- enable-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
embedded-controller@a {
compatible = "microsoft,surface-rt-ec";
reg = <0x0a>;
interrupt-parent = <&gpio>;
interrupts = <74 IRQ_TYPE_EDGE_RISING>;
enable-gpios = <&gpio 88 GPIO_ACTIVE_HIGH>;
monitored-battery = <&battery>;
};
};
...

View File

@@ -163,10 +163,10 @@ static int __init linkstation_poweroff_init(void)
dn = of_find_matching_node(NULL, ls_poweroff_of_match);
if (!dn)
return -ENODEV;
of_node_put(dn);
match = of_match_node(ls_poweroff_of_match, dn);
cfg = match->data;
of_node_put(dn);
dn = of_find_node_by_name(NULL, cfg->mdio_node_name);
if (!dn)

View File

@@ -103,7 +103,7 @@ static int qemu_virt_ctrl_probe(struct platform_device *pdev)
}
static const struct platform_device_id qemu_virt_ctrl_id[] = {
{ "qemu-virt-ctrl", 0 },
{ .name = "qemu-virt-ctrl" },
{ }
};
MODULE_DEVICE_TABLE(platform, qemu_virt_ctrl_id);

View File

@@ -6,6 +6,7 @@
#include <linux/cpu.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -70,11 +71,18 @@ static int sc27xx_poweroff_probe(struct platform_device *pdev)
return 0;
}
static const struct platform_device_id sc27xx_poweroff_id_table[] = {
{ "sc2731-poweroff" },
{ }
};
MODULE_DEVICE_TABLE(platform, sc27xx_poweroff_id_table);
static struct platform_driver sc27xx_poweroff_driver = {
.probe = sc27xx_poweroff_probe,
.driver = {
.name = "sc27xx-poweroff",
},
.id_table = sc27xx_poweroff_id_table,
};
module_platform_driver(sc27xx_poweroff_driver);

View File

@@ -70,8 +70,8 @@ static int spacemit_p1_reboot_probe(struct platform_device *pdev)
}
static const struct platform_device_id spacemit_p1_reboot_id_table[] = {
{ "spacemit-p1-reboot", },
{ /* sentinel */ },
{ .name = "spacemit-p1-reboot" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, spacemit_p1_reboot_id_table);

View File

@@ -9,7 +9,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/reboot.h>
@@ -73,15 +72,12 @@ static const struct of_device_id st_reset_of_match[] = {
static int st_reset_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device *dev = &pdev->dev;
match = of_match_device(st_reset_of_match, dev);
if (!match)
st_restart_syscfg = (struct reset_syscfg *)of_device_get_match_data(dev);
if (!st_restart_syscfg)
return -ENODEV;
st_restart_syscfg = (struct reset_syscfg *)match->data;
st_restart_syscfg->regmap =
syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(st_restart_syscfg->regmap)) {

View File

@@ -41,7 +41,7 @@ static int tps65086_restart_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps65086_restart_id_table[] = {
{ "tps65086-reset", },
{ .name = "tps65086-reset" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table);

View File

@@ -856,6 +856,16 @@ config CHARGER_RK817
help
Say Y to include support for Rockchip RK817 Battery Charger.
config CHARGER_S2M
tristate "Samsung S2M series PMIC battery charger support"
depends on EXTCON_S2M
depends on MFD_SEC_CORE
help
This option enables support for charger devices found in
certain Samsung S2M series PMICs, such as the S2MU005. These
devices provide USB power supply information and also required
for USB OTG role switching.
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
@@ -1135,6 +1145,17 @@ config BATTERY_UG3105
device is off or suspended, the functionality of this driver is
limited to reporting capacity only.
config BATTERY_CHARGER_SURFACE_RT
tristate "Battery & Charger driver for Microsoft Surface RT"
depends on I2C && GPIOLIB
help
UEFI/APX driver for the 1st-generation Microsoft Surface RT
battery. Driver supports reading battery properties and
charger status.
This driver can also be built as a module. If so, the module
will be called surface-rt-ec.
config CHARGER_QCOM_SMB2
tristate "Qualcomm PMI8998 PMIC charger driver"
depends on MFD_SPMI_PMIC

View File

@@ -107,6 +107,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_BQ25980) += bq25980_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_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
@@ -126,6 +127,7 @@ obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o
obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o
obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o
obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o
obj-$(CONFIG_BATTERY_CHARGER_SURFACE_RT) += surface-rt-ec.o
obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o
obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o
obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o

View File

@@ -2037,7 +2037,7 @@ static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
}
/**
* ab8500_fg_batt_ovv_handler() - Battery OVV occured
* ab8500_fg_batt_ovv_handler() - Battery OVV occurred
* @irq: interrupt number
* @_di: pointer to the ab8500_fg structure
*

View File

@@ -727,7 +727,7 @@ static int adp5061_probe(struct i2c_client *client)
}
static const struct i2c_device_id adp5061_id[] = {
{ "adp5061" },
{ .name = "adp5061" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp5061_id);

View File

@@ -955,7 +955,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
static const struct platform_device_id axp288_charger_id_table[] = {
{ .name = "axp288_charger" },
{},
{ }
};
MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);

View File

@@ -799,7 +799,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
static const struct platform_device_id axp288_fg_id_table[] = {
{ .name = DEV_NAME },
{},
{ }
};
MODULE_DEVICE_TABLE(platform, axp288_fg_id_table);

View File

@@ -12,6 +12,7 @@
#include <linux/property.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
/* common defines */
#define BD7182x_MASK_VBAT_U 0x1f
@@ -25,6 +26,7 @@
#define BD71815_MASK_CONF_XSTB BIT(1)
#define BD7182x_MASK_BAT_STAT 0x3f
#define BD7182x_MASK_ILIM 0x3f
#define BD71828_MASK_ILIM_DCIN_EN BIT(6)
#define BD7182x_MASK_DCIN_STAT 0x07
#define BD7182x_MASK_WDT_AUTO 0x40
@@ -1099,10 +1101,75 @@ static int bd7182x_get_rsens(struct bd71828_power *pwr)
return 0;
}
static ssize_t auto_dcin_limit_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bd71828_power *pwr = dev_get_drvdata(dev->parent);
int ret;
unsigned int v;
ret = regmap_read(pwr->regmap, pwr->regs->dcin_set, &v);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", !!(v & BD71828_MASK_ILIM_DCIN_EN));
}
static ssize_t auto_dcin_limit_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct bd71828_power *pwr = dev_get_drvdata(dev->parent);
int ret;
bool v;
ret = kstrtobool(buf, &v);
if (ret < 0)
return ret;
ret = regmap_update_bits(pwr->regmap, BD71828_REG_DCIN_SET,
BD71828_MASK_ILIM_DCIN_EN,
v ? BD71828_MASK_ILIM_DCIN_EN : 0);
if (ret < 0)
return ret;
return len;
}
static DEVICE_ATTR_RW(auto_dcin_limit);
static struct attribute *bd71828_ac_sysfs_attrs[] = {
&dev_attr_auto_dcin_limit.attr,
NULL,
};
static bool bd71828_ac_sysfs_group_visible(struct kobject *kobj)
{
struct device *dev = kobj_to_dev(kobj);
struct bd71828_power *pwr = dev_get_drvdata(dev->parent);
return !!pwr->regs->dcin_set;
}
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(bd71828_ac_sysfs);
static const struct attribute_group bd71828_ac_sysfs_group = {
.attrs = bd71828_ac_sysfs_attrs,
.is_visible = SYSFS_GROUP_VISIBLE(bd71828_ac_sysfs)
};
static const struct attribute_group *bd71828_ac_sysfs_groups[] = {
&bd71828_ac_sysfs_group,
NULL
};
static int bd71828_power_probe(struct platform_device *pdev)
{
struct bd71828_power *pwr;
struct power_supply_config ac_cfg = {};
struct power_supply_config ac_cfg = {
.attr_grp = bd71828_ac_sysfs_groups,
};
struct power_supply_config bat_cfg = {};
int ret;
@@ -1184,10 +1251,10 @@ static int bd71828_power_probe(struct platform_device *pdev)
}
static const struct platform_device_id bd71828_charger_id[] = {
{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
{ "bd72720-power", ROHM_CHIP_TYPE_BD72720 },
{ },
{ .name = "bd71815-power", .driver_data = ROHM_CHIP_TYPE_BD71815 },
{ .name = "bd71828-power", .driver_data = ROHM_CHIP_TYPE_BD71828 },
{ .name = "bd72720-power", .driver_data = ROHM_CHIP_TYPE_BD72720 },
{ }
};
MODULE_DEVICE_TABLE(platform, bd71828_charger_id);

View File

@@ -1738,20 +1738,20 @@ static void bq2415x_remove(struct i2c_client *client)
}
static const struct i2c_device_id bq2415x_i2c_id_table[] = {
{ "bq2415x", BQUNKNOWN },
{ "bq24150", BQ24150 },
{ "bq24150a", BQ24150A },
{ "bq24151", BQ24151 },
{ "bq24151a", BQ24151A },
{ "bq24152", BQ24152 },
{ "bq24153", BQ24153 },
{ "bq24153a", BQ24153A },
{ "bq24155", BQ24155 },
{ "bq24156", BQ24156 },
{ "bq24156a", BQ24156A },
{ "bq24157s", BQ24157S },
{ "bq24158", BQ24158 },
{},
{ .name = "bq2415x", .driver_data = BQUNKNOWN },
{ .name = "bq24150", .driver_data = BQ24150 },
{ .name = "bq24150a", .driver_data = BQ24150A },
{ .name = "bq24151", .driver_data = BQ24151 },
{ .name = "bq24151a", .driver_data = BQ24151A },
{ .name = "bq24152", .driver_data = BQ24152 },
{ .name = "bq24153", .driver_data = BQ24153 },
{ .name = "bq24153a", .driver_data = BQ24153A },
{ .name = "bq24155", .driver_data = BQ24155 },
{ .name = "bq24156", .driver_data = BQ24156 },
{ .name = "bq24156a", .driver_data = BQ24156A },
{ .name = "bq24157s", .driver_data = BQ24157S },
{ .name = "bq24158", .driver_data = BQ24158 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);

View File

@@ -2308,14 +2308,14 @@ static const struct dev_pm_ops bq24190_pm_ops = {
};
static const struct i2c_device_id bq24190_i2c_ids[] = {
{ "bq24190", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] },
{ "bq24192", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] },
{ "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] },
{ "bq24193", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24193] },
{ "bq24196", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] },
{ "bq24296", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] },
{ "bq24297", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] },
{ },
{ .name = "bq24190", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] },
{ .name = "bq24192", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] },
{ .name = "bq24192i", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] },
{ .name = "bq24193", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24193] },
{ .name = "bq24196", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] },
{ .name = "bq24296", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] },
{ .name = "bq24297", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);

View File

@@ -1133,10 +1133,10 @@ static const struct bq2425x_chip_info bq24257_info = {
};
static const struct i2c_device_id bq24257_i2c_ids[] = {
{ "bq24250", (kernel_ulong_t)&bq24250_info },
{ "bq24251", (kernel_ulong_t)&bq24251_info },
{ "bq24257", (kernel_ulong_t)&bq24257_info },
{}
{ .name = "bq24250", .driver_data = (kernel_ulong_t)&bq24250_info },
{ .name = "bq24251", .driver_data = (kernel_ulong_t)&bq24251_info },
{ .name = "bq24257", .driver_data = (kernel_ulong_t)&bq24257_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);

View File

@@ -489,8 +489,8 @@ static int bq24735_charger_probe(struct i2c_client *client)
}
static const struct i2c_device_id bq24735_charger_id[] = {
{ "bq24735-charger" },
{}
{ .name = "bq24735-charger" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);

View File

@@ -1137,9 +1137,9 @@ static const struct bq2515x_info bq25155 = {
};
static const struct i2c_device_id bq2515x_i2c_ids[] = {
{ "bq25150", (kernel_ulong_t)&bq25150 },
{ "bq25155", (kernel_ulong_t)&bq25155 },
{}
{ .name = "bq25150", .driver_data = (kernel_ulong_t)&bq25150 },
{ .name = "bq25155", .driver_data = (kernel_ulong_t)&bq25155 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids);

View File

@@ -1768,14 +1768,14 @@ static int bq256xx_probe(struct i2c_client *client)
}
static const struct i2c_device_id bq256xx_i2c_ids[] = {
{ "bq25600", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] },
{ "bq25600d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] },
{ "bq25601", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] },
{ "bq25601d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] },
{ "bq25611d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] },
{ "bq25618", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] },
{ "bq25619", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] },
{}
{ .name = "bq25600", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] },
{ .name = "bq25600d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] },
{ .name = "bq25601", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] },
{ .name = "bq25601d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] },
{ .name = "bq25611d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] },
{ .name = "bq25618", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] },
{ .name = "bq25619", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq256xx_i2c_ids);

View File

@@ -18,20 +18,34 @@ struct bq257xx_chg;
/**
* struct bq257xx_chip_info - chip specific routines
* @default_iindpm_uA: default input current limit in microamps
* @bq257xx_hw_init: init function for hw
* @bq257xx_hw_shutdown: shutdown function for hw
* @bq257xx_get_state: get and update state of hardware
* @bq257xx_get_ichg: get maximum charge current (in uA)
* @bq257xx_set_ichg: set maximum charge current (in uA)
* @bq257xx_get_vbatreg: get maximum charge voltage (in uV)
* @bq257xx_set_vbatreg: set maximum charge voltage (in uV)
* @bq257xx_get_iindpm: get maximum input current (in uA)
* @bq257xx_set_iindpm: set maximum input current (in uA)
* @bq257xx_get_cur: get battery current from ADC (in uA)
* @bq257xx_get_vbat: get battery voltage from ADC (in uV)
* @bq257xx_get_min_vsys: get minimum system voltage (in uV)
*/
struct bq257xx_chip_info {
int default_iindpm_uA;
int (*bq257xx_hw_init)(struct bq257xx_chg *pdata);
void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata);
int (*bq257xx_get_state)(struct bq257xx_chg *pdata);
int (*bq257xx_get_ichg)(struct bq257xx_chg *pdata, int *intval);
int (*bq257xx_set_ichg)(struct bq257xx_chg *pdata, int ichg);
int (*bq257xx_get_vbatreg)(struct bq257xx_chg *pdata, int *intval);
int (*bq257xx_set_vbatreg)(struct bq257xx_chg *pdata, int vbatreg);
int (*bq257xx_get_iindpm)(struct bq257xx_chg *pdata, int *intval);
int (*bq257xx_set_iindpm)(struct bq257xx_chg *pdata, int iindpm);
int (*bq257xx_get_cur)(struct bq257xx_chg *pdata, int *intval);
int (*bq257xx_get_vbat)(struct bq257xx_chg *pdata, int *intval);
int (*bq257xx_get_min_vsys)(struct bq257xx_chg *pdata, int *intval);
};
/**
@@ -40,8 +54,10 @@ struct bq257xx_chip_info {
* @bq: parent MFD device
* @charger: power supply device
* @online: charger input is present
* @charging: charger is actively charging the battery
* @fast_charge: charger is in fast charge mode
* @pre_charge: charger is in pre-charge mode
* @overvoltage: overvoltage fault detected
* @ov_fault: charger reports over voltage fault
* @batoc_fault: charger reports battery over current fault
* @oc_fault: charger reports over current fault
@@ -57,8 +73,10 @@ struct bq257xx_chg {
struct bq257xx_device *bq;
struct power_supply *charger;
bool online;
bool charging;
bool fast_charge;
bool pre_charge;
bool overvoltage;
bool ov_fault;
bool batoc_fault;
bool oc_fault;
@@ -92,8 +110,10 @@ static int bq25703_get_state(struct bq257xx_chg *pdata)
pdata->online = reg & BQ25703_STS_AC_STAT;
pdata->fast_charge = reg & BQ25703_STS_IN_FCHRG;
pdata->pre_charge = reg & BQ25703_STS_IN_PCHRG;
pdata->charging = pdata->fast_charge || pdata->pre_charge;
pdata->ov_fault = reg & BQ25703_STS_FAULT_ACOV;
pdata->batoc_fault = reg & BQ25703_STS_FAULT_BATOC;
pdata->overvoltage = pdata->ov_fault || pdata->batoc_fault;
pdata->oc_fault = reg & BQ25703_STS_FAULT_ACOC;
return 0;
@@ -128,9 +148,8 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval)
* @vsys: voltage value to set in uV.
*
* This function takes a requested minimum system voltage value, clamps
* it between the minimum supported value by the charger and a user
* defined minimum system value, and then writes the value to the
* appropriate register.
* it between the user defined minimum system value and the maximum supported
* value by the charger, and then writes the value to the appropriate register.
*
* Return: Returns 0 on success or error if an error occurs.
*/
@@ -139,7 +158,7 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys)
unsigned int reg;
int vsys_min = pdata->vsys_min;
vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min);
vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV);
reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV);
reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg);
@@ -465,14 +484,14 @@ static int bq257xx_get_charger_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_STATUS:
if (!pdata->online)
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (pdata->fast_charge || pdata->pre_charge)
else if (pdata->charging)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case POWER_SUPPLY_PROP_HEALTH:
if (pdata->ov_fault || pdata->batoc_fault)
if (pdata->overvoltage)
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
else if (pdata->oc_fault)
val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;
@@ -489,22 +508,22 @@ static int bq257xx_get_charger_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return bq25703_get_iindpm(pdata, &val->intval);
return pdata->chip->bq257xx_get_iindpm(pdata, &val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
return bq25703_get_chrg_volt(pdata, &val->intval);
return pdata->chip->bq257xx_get_vbatreg(pdata, &val->intval);
case POWER_SUPPLY_PROP_CURRENT_NOW:
return bq25703_get_cur(pdata, &val->intval);
return pdata->chip->bq257xx_get_cur(pdata, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
return bq25703_get_vbat(pdata, &val->intval);
return pdata->chip->bq257xx_get_vbat(pdata, &val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
return bq25703_get_ichg_cur(pdata, &val->intval);
return pdata->chip->bq257xx_get_ichg(pdata, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
return bq25703_get_min_vsys(pdata, &val->intval);
return pdata->chip->bq257xx_get_min_vsys(pdata, &val->intval);
case POWER_SUPPLY_PROP_USB_TYPE:
val->intval = pdata->usb_type;
@@ -628,12 +647,19 @@ static const struct power_supply_desc bq257xx_power_supply_desc = {
};
static const struct bq257xx_chip_info bq25703_chip_info = {
.default_iindpm_uA = BQ25703_IINDPM_DEFAULT_UA,
.bq257xx_hw_init = &bq25703_hw_init,
.bq257xx_hw_shutdown = &bq25703_hw_shutdown,
.bq257xx_get_state = &bq25703_get_state,
.bq257xx_get_ichg = &bq25703_get_ichg_cur,
.bq257xx_set_ichg = &bq25703_set_ichg_cur,
.bq257xx_get_vbatreg = &bq25703_get_chrg_volt,
.bq257xx_set_vbatreg = &bq25703_set_chrg_volt,
.bq257xx_get_iindpm = &bq25703_get_iindpm,
.bq257xx_set_iindpm = &bq25703_set_iindpm,
.bq257xx_get_cur = &bq25703_get_cur,
.bq257xx_get_vbat = &bq25703_get_vbat,
.bq257xx_get_min_vsys = &bq25703_get_min_vsys,
};
/**
@@ -676,7 +702,7 @@ static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
"input-current-limit-microamp",
&pdata->iindpm_max);
if (ret)
pdata->iindpm_max = BQ25703_IINDPM_DEFAULT_UA;
pdata->iindpm_max = pdata->chip->default_iindpm_uA;
return 0;
}

View File

@@ -1617,11 +1617,11 @@ static const struct dev_pm_ops bq25890_pm = {
};
static const struct i2c_device_id bq25890_i2c_ids[] = {
{ "bq25890" },
{ "bq25892" },
{ "bq25895" },
{ "bq25896" },
{}
{ .name = "bq25890" },
{ .name = "bq25892" },
{ .name = "bq25895" },
{ .name = "bq25896" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);

View File

@@ -1266,10 +1266,10 @@ static int bq25980_probe(struct i2c_client *client)
}
static const struct i2c_device_id bq25980_i2c_ids[] = {
{ "bq25980", BQ25980 },
{ "bq25975", BQ25975 },
{ "bq25960", BQ25960 },
{},
{ .name = "bq25980", .driver_data = BQ25980 },
{ .name = "bq25975", .driver_data = BQ25975 },
{ .name = "bq25960", .driver_data = BQ25960 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids);

View File

@@ -225,38 +225,38 @@ static void bq27xxx_battery_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27200", BQ27000 },
{ "bq27210", BQ27010 },
{ "bq27500", BQ2750X },
{ "bq27510", BQ2751X },
{ "bq27520", BQ2752X },
{ "bq27500-1", BQ27500 },
{ "bq27510g1", BQ27510G1 },
{ "bq27510g2", BQ27510G2 },
{ "bq27510g3", BQ27510G3 },
{ "bq27520g1", BQ27520G1 },
{ "bq27520g2", BQ27520G2 },
{ "bq27520g3", BQ27520G3 },
{ "bq27520g4", BQ27520G4 },
{ "bq27521", BQ27521 },
{ "bq27530", BQ27530 },
{ "bq27531", BQ27531 },
{ "bq27541", BQ27541 },
{ "bq27542", BQ27542 },
{ "bq27546", BQ27546 },
{ "bq27742", BQ27742 },
{ "bq27545", BQ27545 },
{ "bq27411", BQ27411 },
{ "bq27421", BQ27421 },
{ "bq27425", BQ27425 },
{ "bq27426", BQ27426 },
{ "bq27441", BQ27441 },
{ "bq27621", BQ27621 },
{ "bq27z561", BQ27Z561 },
{ "bq28z610", BQ28Z610 },
{ "bq34z100", BQ34Z100 },
{ "bq78z100", BQ78Z100 },
{},
{ .name = "bq27200", .driver_data = BQ27000 },
{ .name = "bq27210", .driver_data = BQ27010 },
{ .name = "bq27500", .driver_data = BQ2750X },
{ .name = "bq27510", .driver_data = BQ2751X },
{ .name = "bq27520", .driver_data = BQ2752X },
{ .name = "bq27500-1", .driver_data = BQ27500 },
{ .name = "bq27510g1", .driver_data = BQ27510G1 },
{ .name = "bq27510g2", .driver_data = BQ27510G2 },
{ .name = "bq27510g3", .driver_data = BQ27510G3 },
{ .name = "bq27520g1", .driver_data = BQ27520G1 },
{ .name = "bq27520g2", .driver_data = BQ27520G2 },
{ .name = "bq27520g3", .driver_data = BQ27520G3 },
{ .name = "bq27520g4", .driver_data = BQ27520G4 },
{ .name = "bq27521", .driver_data = BQ27521 },
{ .name = "bq27530", .driver_data = BQ27530 },
{ .name = "bq27531", .driver_data = BQ27531 },
{ .name = "bq27541", .driver_data = BQ27541 },
{ .name = "bq27542", .driver_data = BQ27542 },
{ .name = "bq27546", .driver_data = BQ27546 },
{ .name = "bq27742", .driver_data = BQ27742 },
{ .name = "bq27545", .driver_data = BQ27545 },
{ .name = "bq27411", .driver_data = BQ27411 },
{ .name = "bq27421", .driver_data = BQ27421 },
{ .name = "bq27425", .driver_data = BQ27425 },
{ .name = "bq27426", .driver_data = BQ27426 },
{ .name = "bq27441", .driver_data = BQ27441 },
{ .name = "bq27621", .driver_data = BQ27621 },
{ .name = "bq27z561", .driver_data = BQ27Z561 },
{ .name = "bq28z610", .driver_data = BQ28Z610 },
{ .name = "bq34z100", .driver_data = BQ34Z100 },
{ .name = "bq78z100", .driver_data = BQ78Z100 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);

View File

@@ -303,8 +303,10 @@ static bool is_full_charged(struct charger_manager *cm)
if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
&& desc->fullbatt_vchkdrop_uV)
uV += desc->fullbatt_vchkdrop_uV;
if (uV >= desc->fullbatt_uV)
return true;
if (uV >= desc->fullbatt_uV) {
is_full = true;
goto out;
}
}
}
@@ -1649,8 +1651,8 @@ static void charger_manager_remove(struct platform_device *pdev)
}
static const struct platform_device_id charger_manager_id[] = {
{ "charger-manager", 0 },
{ },
{ .name = "charger-manager" },
{ }
};
MODULE_DEVICE_TABLE(platform, charger_manager_id);

View File

@@ -439,10 +439,13 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata)
if (IS_ERR_OR_NULL(nvmem)) {
ddata->check_nvmem = true;
dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n");
} else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
battery_id = 0;
ddata->check_nvmem = true;
dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
} else {
if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
battery_id = 0;
ddata->check_nvmem = true;
dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
}
nvmem_device_put(nvmem);
}
switch (battery_id) {

View File

@@ -21,6 +21,7 @@
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>

View File

@@ -320,9 +320,10 @@ static int cros_chctl_probe(struct platform_device *pdev)
}
static const struct platform_device_id cros_chctl_id[] = {
{ "cros-charge-control", 0 },
{}
{ .name = "cros-charge-control" },
{ }
};
MODULE_DEVICE_TABLE(platform, cros_chctl_id);
static struct platform_driver cros_chctl_driver = {
.driver.name = "cros-charge-control",
@@ -331,7 +332,6 @@ static struct platform_driver cros_chctl_driver = {
};
module_platform_driver(cros_chctl_driver);
MODULE_DEVICE_TABLE(platform, cros_chctl_id);
MODULE_DESCRIPTION("ChromeOS EC charge control");
MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net>");
MODULE_LICENSE("GPL");

View File

@@ -369,8 +369,8 @@ static int __maybe_unused cros_pchg_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(cros_pchg_pm_ops, NULL, cros_pchg_resume);
static const struct platform_device_id cros_pchg_id[] = {
{ DRV_NAME, 0 },
{}
{ .name = DRV_NAME },
{ }
};
MODULE_DEVICE_TABLE(platform, cros_pchg_id);

View File

@@ -707,8 +707,8 @@ static SIMPLE_DEV_PM_OPS(cros_usbpd_charger_pm_ops, NULL,
cros_usbpd_charger_resume);
static const struct platform_device_id cros_usbpd_charger_id[] = {
{ DRV_NAME, 0 },
{}
{ .name = DRV_NAME },
{ }
};
MODULE_DEVICE_TABLE(platform, cros_usbpd_charger_id);

View File

@@ -733,7 +733,7 @@ static int __maybe_unused cw_bat_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume);
static const struct i2c_device_id cw_bat_id_table[] = {
{ "cw2015" },
{ .name = "cw2015" },
{ }
};

View File

@@ -423,9 +423,9 @@ static int ds278x_battery_probe(struct i2c_client *client)
}
static const struct i2c_device_id ds278x_id[] = {
{"ds2782", DS2782},
{"ds2786", DS2786},
{},
{ .name = "ds2782", .driver_data = DS2782 },
{ .name = "ds2786", .driver_data = DS2786 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds278x_id);

View File

@@ -584,7 +584,7 @@ static const struct of_device_id lp8727_dt_ids[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, lp8727_dt_ids);
static const struct i2c_device_id lp8727_ids[] = {
{ "lp8727" },
{ .name = "lp8727" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp8727_ids);

View File

@@ -600,11 +600,11 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
static const struct i2c_device_id ltc294x_i2c_id[] = {
{ "ltc2941", LTC2941_ID, },
{ "ltc2942", LTC2942_ID, },
{ "ltc2943", LTC2943_ID, },
{ "ltc2944", LTC2944_ID, },
{ },
{ .name = "ltc2941", .driver_data = LTC2941_ID },
{ .name = "ltc2942", .driver_data = LTC2942_ID },
{ .name = "ltc2943", .driver_data = LTC2943_ID },
{ .name = "ltc2944", .driver_data = LTC2944_ID },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);

View File

@@ -1229,10 +1229,10 @@ static void ltc4162l_alert(struct i2c_client *client,
}
static const struct i2c_device_id ltc4162l_i2c_id_table[] = {
{ "ltc4015", (kernel_ulong_t)&ltc4015_chip_info },
{ "ltc4162-f", (kernel_ulong_t)&ltc4162f_chip_info },
{ "ltc4162-l", (kernel_ulong_t)&ltc4162l_chip_info },
{ "ltc4162-s", (kernel_ulong_t)&ltc4162s_chip_info },
{ .name = "ltc4015", .driver_data = (kernel_ulong_t)&ltc4015_chip_info },
{ .name = "ltc4162-f", .driver_data = (kernel_ulong_t)&ltc4162f_chip_info },
{ .name = "ltc4162-l", .driver_data = (kernel_ulong_t)&ltc4162l_chip_info },
{ .name = "ltc4162-s", .driver_data = (kernel_ulong_t)&ltc4162s_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table);

View File

@@ -834,7 +834,7 @@ static void macsmc_power_remove(struct platform_device *pdev)
}
static const struct platform_device_id macsmc_power_id[] = {
{ "macsmc-power" },
{ .name = "macsmc-power" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, macsmc_power_id);

View File

@@ -613,18 +613,16 @@ static void max14577_charger_remove(struct platform_device *pdev)
}
static const struct platform_device_id max14577_charger_id[] = {
{ "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
{ .name = "max14577-charger" },
{ .name = "max77836-charger" },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_charger_id);
static const struct of_device_id of_max14577_charger_dt_match[] = {
{ .compatible = "maxim,max14577-charger",
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577, },
{ .compatible = "maxim,max77836-charger",
.data = (void *)MAXIM_DEVICE_TYPE_MAX77836, },
{ },
{ .compatible = "maxim,max14577-charger" },
{ .compatible = "maxim,max77836-charger" },
{ }
};
MODULE_DEVICE_TABLE(of, of_max14577_charger_dt_match);

View File

@@ -300,8 +300,8 @@ static int max14656_probe(struct i2c_client *client)
}
static const struct i2c_device_id max14656_id[] = {
{ "max14656" },
{}
{ .name = "max14656" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max14656_id);

View File

@@ -598,15 +598,15 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id max17040_id[] = {
{ "max17040", ID_MAX17040 },
{ "max17041", ID_MAX17041 },
{ "max17043", ID_MAX17043 },
{ "max77836-battery", ID_MAX17043 },
{ "max17044", ID_MAX17044 },
{ "max17048", ID_MAX17048 },
{ "max17049", ID_MAX17049 },
{ "max17058", ID_MAX17058 },
{ "max17059", ID_MAX17059 },
{ .name = "max17040", .driver_data = ID_MAX17040 },
{ .name = "max17041", .driver_data = ID_MAX17041 },
{ .name = "max17043", .driver_data = ID_MAX17043 },
{ .name = "max77836-battery", .driver_data = ID_MAX17043 },
{ .name = "max17044", .driver_data = ID_MAX17044 },
{ .name = "max17048", .driver_data = ID_MAX17048 },
{ .name = "max17049", .driver_data = ID_MAX17049 },
{ .name = "max17058", .driver_data = ID_MAX17058 },
{ .name = "max17059", .driver_data = ID_MAX17059 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, max17040_id);

View File

@@ -27,6 +27,7 @@
/* Status register bits */
#define STATUS_POR_BIT (1 << 1)
#define STATUS_BST_BIT (1 << 3)
#define STATUS_DSOCI_BIT (1 << 7)
#define STATUS_VMN_BIT (1 << 8)
#define STATUS_TMN_BIT (1 << 9)
#define STATUS_SMN_BIT (1 << 10)
@@ -38,6 +39,7 @@
/* Interrupt mask bits */
#define CFG_ALRT_BIT_ENBL (1 << 2)
#define CFG2_DSOCI_BIT_ENBL (1 << 7)
#define VFSOC0_LOCK 0x0000
#define VFSOC0_UNLOCK 0x0080
@@ -52,16 +54,32 @@
#define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */
#define MAX17042_CRITICAL_SOC 0x03
#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */
#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */
#define MAX17042_TIME_LSB 5625 / 1000 /* s */
#define MAX17042_VOLTAGE_LSB 625 / 8 /* µV */
#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */
#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */
struct max17042_chip {
struct device *dev;
struct regmap *regmap;
struct power_supply *battery;
enum max170xx_chip_type chip_type;
struct max17042_platform_data *pdata;
struct max17042_config_data *config_data;
struct work_struct work;
int init_complete;
int irq;
int task_period;
bool enable_current_sense;
bool enable_por_init;
unsigned int r_sns;
int vmin; /* in millivolts */
int vmax; /* in millivolts */
int temp_min; /* in tenths of degree Celsius */
int temp_max; /* in tenths of degree Celsius */
};
static enum power_supply_property max17042_battery_props[] = {
@@ -107,15 +125,14 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
*temp = sign_extend32(data, 15);
/* The value is converted into deci-centigrade scale */
/* Units of LSB = 1 / 256 degree Celsius */
*temp = *temp * 10 / 256;
*temp = *temp * 10 * MAX17042_TEMPERATURE_LSB;
return 0;
}
static int max17042_get_status(struct max17042_chip *chip, int *status)
{
int ret, charge_full, charge_now;
int avg_current;
int current_now;
u32 data;
ret = power_supply_am_i_supplied(chip->battery);
@@ -155,19 +172,18 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
* Even though we are supplied, we may still be discharging if the
* supply is e.g. only delivering 5V 0.5A. Check current if available.
*/
if (!chip->pdata->enable_current_sense) {
if (!chip->enable_current_sense) {
*status = POWER_SUPPLY_STATUS_CHARGING;
return 0;
}
ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data);
ret = regmap_read(chip->regmap, MAX17042_Current, &data);
if (ret < 0)
return ret;
avg_current = sign_extend32(data, 15);
avg_current *= 1562500 / chip->pdata->r_sns;
current_now = sign_extend32(data, 15);
if (avg_current > 0)
if (current_now > 0)
*status = POWER_SUPPLY_STATUS_CHARGING;
else
*status = POWER_SUPPLY_STATUS_DISCHARGING;
@@ -185,7 +201,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
vavg = val * 625 / 8;
vavg = val * MAX17042_VOLTAGE_LSB;
/* Convert to millivolts */
vavg /= 1000;
@@ -194,16 +210,16 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
goto health_error;
/* bits [0-3] unused */
vbatt = val * 625 / 8;
vbatt = val * MAX17042_VOLTAGE_LSB;
/* Convert to millivolts */
vbatt /= 1000;
if (vavg < chip->pdata->vmin) {
if (vavg < chip->vmin) {
*health = POWER_SUPPLY_HEALTH_DEAD;
goto out;
}
if (vbatt > size_add(chip->pdata->vmax, MAX17042_VMAX_TOLERANCE)) {
if (vbatt > size_add(chip->vmax, MAX17042_VMAX_TOLERANCE)) {
*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
goto out;
}
@@ -212,12 +228,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
if (ret < 0)
goto health_error;
if (temp < chip->pdata->temp_min) {
if (temp < chip->temp_min) {
*health = POWER_SUPPLY_HEALTH_COLD;
goto out;
}
if (temp > chip->pdata->temp_max) {
if (temp > chip->temp_max) {
*health = POWER_SUPPLY_HEALTH_OVERHEAT;
goto out;
}
@@ -301,24 +317,24 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
val->intval = data * 625 / 8;
val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
ret = regmap_read(map, MAX17042_AvgVCELL, &data);
if (ret < 0)
return ret;
val->intval = data * 625 / 8;
val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = regmap_read(map, MAX17042_OCVInternal, &data);
if (ret < 0)
return ret;
val->intval = data * 625 / 8;
val->intval = data * MAX17042_VOLTAGE_LSB;
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (chip->pdata->enable_current_sense)
if (chip->enable_current_sense)
ret = regmap_read(map, MAX17042_RepSOC, &data);
else
ret = regmap_read(map, MAX17042_VFSOC, &data);
@@ -332,10 +348,10 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
data64 = data * 5000000ll;
data64 = data * MAX17042_CAPACITY_LSB;
data64 *= chip->task_period;
do_div(data64, MAX17042_DEFAULT_TASK_PERIOD);
do_div(data64, chip->pdata->r_sns);
do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
@@ -343,10 +359,10 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
data64 = data * 5000000ll;
data64 = data * MAX17042_CAPACITY_LSB;
data64 *= chip->task_period;
do_div(data64, MAX17042_DEFAULT_TASK_PERIOD);
do_div(data64, chip->pdata->r_sns);
do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
@@ -354,10 +370,10 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
data64 = data * 5000000ll;
data64 = data * MAX17042_CAPACITY_LSB;
data64 *= chip->task_period;
do_div(data64, MAX17042_DEFAULT_TASK_PERIOD);
do_div(data64, chip->pdata->r_sns);
do_div(data64, chip->r_sns);
val->intval = data64;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
@@ -365,10 +381,10 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
data64 = sign_extend64(data, 15) * 5000000ll;
data64 = sign_extend64(data, 15) * MAX17042_CAPACITY_LSB;
data64 *= chip->task_period;
data64 = div_s64(data64, MAX17042_DEFAULT_TASK_PERIOD);
val->intval = div_s64(data64, chip->pdata->r_sns);
val->intval = div_s64(data64, chip->r_sns);
break;
case POWER_SUPPLY_PROP_TEMP:
ret = max17042_get_temperature(chip, &val->intval);
@@ -390,10 +406,10 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = sign_extend32(data >> 8, 7) * 10;
break;
case POWER_SUPPLY_PROP_TEMP_MIN:
val->intval = chip->pdata->temp_min;
val->intval = chip->temp_min;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
val->intval = chip->pdata->temp_max;
val->intval = chip->temp_max;
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = max17042_get_battery_health(chip, &val->intval);
@@ -404,25 +420,25 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (chip->pdata->enable_current_sense) {
if (chip->enable_current_sense) {
ret = regmap_read(map, MAX17042_Current, &data);
if (ret < 0)
return ret;
data64 = sign_extend64(data, 15) * 1562500ll;
val->intval = div_s64(data64, chip->pdata->r_sns);
data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->r_sns);
} else {
return -EINVAL;
}
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
if (chip->pdata->enable_current_sense) {
if (chip->enable_current_sense) {
ret = regmap_read(map, MAX17042_AvgCurrent, &data);
if (ret < 0)
return ret;
data64 = sign_extend64(data, 15) * 1562500ll;
val->intval = div_s64(data64, chip->pdata->r_sns);
data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->r_sns);
} else {
return -EINVAL;
}
@@ -432,8 +448,8 @@ static int max17042_get_property(struct power_supply *psy,
if (ret < 0)
return ret;
data64 = data * 1562500ll;
val->intval = div_s64(data64, chip->pdata->r_sns);
data64 = data * MAX17042_CURRENT_LSB;
val->intval = div_s64(data64, chip->r_sns);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = regmap_read(map, MAX17042_TTE, &data);
@@ -444,7 +460,7 @@ static int max17042_get_property(struct power_supply *psy,
if (data == U16_MAX)
return -ENODATA;
val->intval = data * 5625 / 1000;
val->intval = data * MAX17042_TIME_LSB;
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 &&
@@ -459,7 +475,7 @@ static int max17042_get_property(struct power_supply *psy,
if (data == U16_MAX)
return -ENODATA;
val->intval = data * 5625 / 1000;
val->intval = data * MAX17042_TIME_LSB;
break;
default:
return -EINVAL;
@@ -582,7 +598,7 @@ static inline void max17042_write_model_data(struct max17042_chip *chip,
for (i = 0; i < size; i++)
regmap_write(map, addr + i,
chip->pdata->config_data->cell_char_tbl[i]);
chip->config_data->cell_char_tbl[i]);
}
static inline void max17042_read_model_data(struct max17042_chip *chip,
@@ -617,7 +633,7 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip,
static int max17042_init_model(struct max17042_chip *chip)
{
int ret;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl);
u16 *temp_data;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
@@ -632,7 +648,7 @@ static int max17042_init_model(struct max17042_chip *chip)
ret = max17042_model_data_compare(
chip,
chip->pdata->config_data->cell_char_tbl,
chip->config_data->cell_char_tbl,
temp_data,
table_size);
@@ -645,7 +661,7 @@ static int max17042_init_model(struct max17042_chip *chip)
static int max17042_verify_model_lock(struct max17042_chip *chip)
{
int i;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl);
u16 *temp_data;
int ret = 0;
@@ -665,7 +681,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
static void max17042_write_config_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
regmap_write(map, MAX17042_CONFIG, config->config);
@@ -683,7 +699,7 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
static void max17042_write_custom_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
@@ -707,7 +723,7 @@ static void max17042_write_custom_regs(struct max17042_chip *chip)
static void max17042_update_capacity_regs(struct max17042_chip *chip)
{
struct max17042_config_data *config = chip->pdata->config_data;
struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
max17042_write_verify_reg(map, MAX17042_FullCAP,
@@ -733,7 +749,7 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
u32 full_cap0, rep_cap, dq_acc, vfSoc;
u32 rem_cap;
struct max17042_config_data *config = chip->pdata->config_data;
struct max17042_config_data *config = chip->config_data;
struct regmap *map = chip->regmap;
regmap_read(map, MAX17042_FullCAP0, &full_cap0);
@@ -765,14 +781,14 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
}
/*
* Block write all the override values coming from platform data.
* Block write all the override values coming from config_data.
* This function MUST be called before the POR initialization procedure
* specified by maxim.
*/
static inline void max17042_override_por_values(struct max17042_chip *chip)
{
struct regmap *map = chip->regmap;
struct max17042_config_data *config = chip->pdata->config_data;
struct max17042_config_data *config = chip->config_data;
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
max17042_override_por(map, MAX17042_TOFF, config->toff);
@@ -827,6 +843,9 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) {
max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
max17042_override_por(map, MAX17055_ModelCfg, config->model_cfg);
}
static int max17042_init_chip(struct max17042_chip *chip)
@@ -835,45 +854,54 @@ static int max17042_init_chip(struct max17042_chip *chip)
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);
}
/* After Power up, the MAX17042 requires 500mS in order
* to perform signal debouncing and initial SOC reporting
*/
msleep(500);
/* Initialize configuration */
max17042_write_config_regs(chip);
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) {
/* Initialize configuration */
max17042_write_config_regs(chip);
/* write cell characterization data */
ret = max17042_init_model(chip);
if (ret) {
dev_err(chip->dev, "%s init failed\n",
__func__);
return -EIO;
/* write cell characterization data */
ret = max17042_init_model(chip);
if (ret) {
dev_err(chip->dev, "%s init failed\n",
__func__);
return -EIO;
}
ret = max17042_verify_model_lock(chip);
if (ret) {
dev_err(chip->dev, "%s lock verify failed\n",
__func__);
return -EIO;
}
/* write custom parameters */
max17042_write_custom_regs(chip);
/* update capacity params */
max17042_update_capacity_regs(chip);
/* delay must be atleast 350mS to allow VFSOC
* to be calculated from the new configuration
*/
msleep(350);
/* reset vfsoc0 reg */
max17042_reset_vfsoc0_reg(chip);
/* load new capacity params */
max17042_load_new_capacity_params(chip);
}
ret = max17042_verify_model_lock(chip);
if (ret) {
dev_err(chip->dev, "%s lock verify failed\n",
__func__);
return -EIO;
}
/* write custom parameters */
max17042_write_custom_regs(chip);
/* update capacity params */
max17042_update_capacity_regs(chip);
/* delay must be atleast 350mS to allow VFSOC
* to be calculated from the new configuration
*/
msleep(350);
/* reset vfsoc0 reg */
max17042_reset_vfsoc0_reg(chip);
/* load new capacity params */
max17042_load_new_capacity_params(chip);
/* Init complete, Clear the POR bit */
regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
return 0;
@@ -887,7 +915,7 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
/* program interrupt thresholds such that we should
* get interrupt for every 'off' perc change in the soc
*/
if (chip->pdata->enable_current_sense)
if (chip->enable_current_sense)
regmap_read(map, MAX17042_RepSOC, &soc);
else
regmap_read(map, MAX17042_VFSOC, &soc);
@@ -898,6 +926,44 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
regmap_write(map, MAX17042_SALRT_Th, soc_tr);
}
static void max17042_set_critical_soc_threshold(struct max17042_chip *chip)
{
struct regmap *map = chip->regmap;
u32 soc;
if (chip->enable_current_sense)
regmap_read(map, MAX17042_RepSOC, &soc);
else
regmap_read(map, MAX17042_VFSOC, &soc);
regmap_write(map, MAX17042_SALRT_Th,
((soc >> 8) >= MAX17042_CRITICAL_SOC) ?
0xff00 + MAX17042_CRITICAL_SOC : 0xff00);
}
static void max17042_enable_soc_alerts(struct max17042_chip *chip)
{
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) {
regmap_update_bits(chip->regmap, MAX17055_Config2,
CFG2_DSOCI_BIT_ENBL,
CFG2_DSOCI_BIT_ENBL);
max17042_set_critical_soc_threshold(chip);
return;
}
max17042_set_soc_threshold(chip, 1);
}
static void max17042_suspend_soc_alerts(struct max17042_chip *chip)
{
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055)
return;
regmap_update_bits(chip->regmap, MAX17055_Config2,
CFG2_DSOCI_BIT_ENBL, 0);
max17042_set_critical_soc_threshold(chip);
}
static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
@@ -908,9 +974,10 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
if (ret)
return IRQ_HANDLED;
if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) {
if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT) ||
(val & STATUS_DSOCI_BIT)) {
dev_dbg(chip->dev, "SOC threshold INTR\n");
max17042_set_soc_threshold(chip, 1);
max17042_enable_soc_alerts(chip);
}
/* we implicitly handle all alerts via power_supply_changed */
@@ -927,8 +994,8 @@ static void max17042_init_worker(struct work_struct *work)
struct max17042_chip, work);
int ret;
/* Initialize registers according to values from the platform data */
if (chip->pdata->enable_por_init && chip->pdata->config_data) {
/* Initialize registers according to values from config_data */
if (chip->enable_por_init && chip->config_data) {
ret = max17042_init_chip(chip);
if (ret)
return;
@@ -938,17 +1005,10 @@ static void max17042_init_worker(struct work_struct *work)
}
#ifdef CONFIG_OF
static struct max17042_platform_data *
max17042_get_of_pdata(struct max17042_chip *chip)
static int max17042_parse_dt(struct max17042_chip *chip)
{
struct device *dev = chip->dev;
struct device_node *np = dev->of_node;
struct device_node *np = chip->dev->of_node;
u32 prop;
struct max17042_platform_data *pdata;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
/*
* Require current sense resistor value to be specified for
@@ -959,85 +1019,46 @@ max17042_get_of_pdata(struct max17042_chip *chip)
if ((of_property_read_u32(np, "shunt-resistor-micro-ohms",
&prop) == 0) ||
(of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0)) {
pdata->r_sns = prop;
pdata->enable_current_sense = true;
chip->r_sns = prop;
chip->enable_current_sense = true;
}
if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
pdata->temp_min = INT_MIN;
if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
pdata->temp_max = INT_MAX;
if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
pdata->vmin = INT_MIN;
if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
pdata->vmax = INT_MAX;
if (of_property_read_s32(np, "maxim,cold-temp", &chip->temp_min))
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))
chip->vmin = INT_MIN;
if (of_property_read_s32(np, "maxim,over-volt", &chip->vmax))
chip->vmax = INT_MAX;
return pdata;
return 0;
}
#endif
static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
/*
* Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
* when the voltage FG reports 95%, as recommended in the datasheet.
*/
{ MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 },
};
static struct max17042_platform_data *
max17042_get_default_pdata(struct max17042_chip *chip)
static int max17042_init_defaults(struct max17042_chip *chip)
{
struct device *dev = chip->dev;
struct max17042_platform_data *pdata;
int ret, misc_cfg;
/*
* The MAX17047 gets used on x86 where we might not have pdata, assume
* The MAX17047 gets used on x86 where we might not have DT, assume
* the firmware will already have initialized the fuel-gauge and provide
* default values for the non init bits to make things work.
*/
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return pdata;
if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
pdata->init_data = max17047_default_pdata_init_regs;
pdata->num_init_data =
ARRAY_SIZE(max17047_default_pdata_init_regs);
}
ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg);
if (ret < 0)
return NULL;
return ret;
/* If bits 0-1 are set to 3 then only Voltage readings are used */
if ((misc_cfg & 0x3) == 0x3)
pdata->enable_current_sense = false;
else
pdata->enable_current_sense = true;
chip->enable_current_sense = (misc_cfg & 0x3) != 0x3;
pdata->vmin = MAX17042_DEFAULT_VMIN;
pdata->vmax = MAX17042_DEFAULT_VMAX;
pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN;
pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX;
chip->vmin = MAX17042_DEFAULT_VMIN;
chip->vmax = MAX17042_DEFAULT_VMAX;
chip->temp_min = MAX17042_DEFAULT_TEMP_MIN;
chip->temp_max = MAX17042_DEFAULT_TEMP_MAX;
return pdata;
}
static struct max17042_platform_data *
max17042_get_pdata(struct max17042_chip *chip)
{
struct device *dev = chip->dev;
#ifdef CONFIG_OF
if (dev->of_node)
return max17042_get_of_pdata(chip);
#endif
if (dev->platform_data)
return dev->platform_data;
return max17042_get_default_pdata(chip);
return 0;
}
static const struct regmap_config max17042_regmap_config = {
@@ -1116,8 +1137,8 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
struct power_supply_config psy_cfg = {};
struct max17042_chip *chip;
int ret;
int i;
u32 val;
bool use_default_config = false;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
@@ -1138,10 +1159,19 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
return dev_err_probe(dev, PTR_ERR(chip->regmap),
"Failed to initialize regmap\n");
chip->pdata = max17042_get_pdata(chip);
if (!chip->pdata)
return dev_err_probe(dev, -EINVAL,
"no platform data provided\n");
#ifdef CONFIG_OF
if (dev->of_node) {
ret = max17042_parse_dt(chip);
if (ret)
return ret;
} else
#endif
{
ret = max17042_init_defaults(chip);
if (ret)
return ret;
use_default_config = true;
}
dev_set_drvdata(dev, chip);
psy_cfg.drv_data = chip;
@@ -1149,19 +1179,13 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
/* When current is not measured,
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */
if (!chip->pdata->enable_current_sense)
if (!chip->enable_current_sense)
max17042_desc = &max17042_no_current_sense_psy_desc;
if (chip->pdata->r_sns == 0)
chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
if (chip->r_sns == 0)
chip->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
if (chip->pdata->init_data)
for (i = 0; i < chip->pdata->num_init_data; i++)
regmap_write(chip->regmap,
chip->pdata->init_data[i].addr,
chip->pdata->init_data[i].data);
if (!chip->pdata->enable_current_sense) {
if (!chip->enable_current_sense) {
regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
@@ -1184,6 +1208,16 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
return dev_err_probe(dev, PTR_ERR(chip->battery),
"failed: power supply register\n");
/*
* Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
* when the voltage FG reports 95%, as recommended in the datasheet.
*/
if (use_default_config &&
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))
regmap_write(chip->regmap, MAX17047_FullSOCThr,
MAX17042_BATTERY_FULL << 8);
if (irq) {
unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED;
@@ -1196,7 +1230,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
regmap_update_bits(chip->regmap, MAX17042_CONFIG,
CFG_ALRT_BIT_ENBL,
CFG_ALRT_BIT_ENBL);
max17042_set_soc_threshold(chip, 1);
max17042_enable_soc_alerts(chip);
} else {
irq = 0;
if (ret != -EBUSY)
@@ -1254,14 +1288,14 @@ static int max17042_platform_probe(struct platform_device *pdev)
if (!i2c)
return -EINVAL;
dev->of_node = dev->parent->of_node;
device_set_of_node_from_dev(dev, dev->parent);
id = platform_get_device_id(pdev);
irq = platform_get_irq(pdev, 0);
return max17042_probe(i2c, dev, irq, id->driver_data);
}
#ifdef CONFIG_PM_SLEEP
static int max17042_suspend(struct device *dev)
{
struct max17042_chip *chip = dev_get_drvdata(dev);
@@ -1272,6 +1306,7 @@ static int max17042_suspend(struct device *dev)
*/
if (chip->irq) {
disable_irq(chip->irq);
max17042_suspend_soc_alerts(chip);
enable_irq_wake(chip->irq);
}
@@ -1285,16 +1320,15 @@ static int max17042_resume(struct device *dev)
if (chip->irq) {
disable_irq_wake(chip->irq);
enable_irq(chip->irq);
/* re-program the SOC thresholds to 1% change */
max17042_set_soc_threshold(chip, 1);
/* re-arm runtime SOC alerts */
max17042_enable_soc_alerts(chip);
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
max17042_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
max17042_resume);
#ifdef CONFIG_ACPI
static const struct acpi_device_id max17042_acpi_match[] = {
@@ -1335,12 +1369,12 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match);
#endif
static const struct i2c_device_id max17042_id[] = {
{ "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
{ "max77759-fg", MAXIM_DEVICE_TYPE_MAX77759 },
{ "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 },
{ .name = "max17042", .driver_data = MAXIM_DEVICE_TYPE_MAX17042 },
{ .name = "max17047", .driver_data = MAXIM_DEVICE_TYPE_MAX17047 },
{ .name = "max17050", .driver_data = MAXIM_DEVICE_TYPE_MAX17050 },
{ .name = "max17055", .driver_data = MAXIM_DEVICE_TYPE_MAX17055 },
{ .name = "max77759-fg", .driver_data = MAXIM_DEVICE_TYPE_MAX77759 },
{ .name = "max77849-battery", .driver_data = MAXIM_DEVICE_TYPE_MAX17047 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17042_id);
@@ -1361,7 +1395,7 @@ static struct i2c_driver max17042_i2c_driver = {
.name = "max17042",
.acpi_match_table = ACPI_PTR(max17042_acpi_match),
.of_match_table = of_match_ptr(max17042_dt_match),
.pm = &max17042_pm_ops,
.pm = pm_ptr(&max17042_pm_ops),
},
.probe = max17042_i2c_probe,
.id_table = max17042_id,
@@ -1371,7 +1405,7 @@ static struct platform_driver max17042_platform_driver = {
.driver = {
.name = "max17042",
.acpi_match_table = ACPI_PTR(max17042_acpi_match),
.pm = &max17042_pm_ops,
.pm = pm_ptr(&max17042_pm_ops),
},
.probe = max17042_platform_probe,
.id_table = max17042_platform_id,

View File

@@ -788,7 +788,7 @@ static void max77693_charger_remove(struct platform_device *pdev)
}
static const struct platform_device_id max77693_charger_id[] = {
{ "max77693-charger", 0, },
{ .name = "max77693-charger" },
{ }
};
MODULE_DEVICE_TABLE(platform, max77693_charger_id);

View File

@@ -754,7 +754,7 @@ static int max77759_charger_probe(struct platform_device *pdev)
}
static const struct platform_device_id max77759_charger_id[] = {
{ .name = "max77759-charger", },
{ .name = "max77759-charger" },
{ }
};
MODULE_DEVICE_TABLE(platform, max77759_charger_id);

View File

@@ -484,7 +484,7 @@ static int max77976_probe(struct i2c_client *client)
}
static const struct i2c_device_id max77976_i2c_id[] = {
{ MAX77976_DRIVER_NAME },
{ .name = MAX77976_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);

View File

@@ -731,7 +731,7 @@ static const struct of_device_id max8971_match_ids[] = {
MODULE_DEVICE_TABLE(of, max8971_match_ids);
static const struct i2c_device_id max8971_i2c_id[] = {
{ "max8971" },
{ .name = "max8971" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max8971_i2c_id);

View File

@@ -268,7 +268,7 @@ static int max8997_battery_probe(struct platform_device *pdev)
}
static const struct platform_device_id max8997_battery_id[] = {
{ "max8997-battery", 0 },
{ .name = "max8997-battery" },
{ }
};
MODULE_DEVICE_TABLE(platform, max8997_battery_id);

View File

@@ -188,7 +188,7 @@ static int max8998_battery_probe(struct platform_device *pdev)
}
static const struct platform_device_id max8998_battery_id[] = {
{ "max8998-battery", TYPE_MAX8998 },
{ .name = "max8998-battery" },
{ }
};
MODULE_DEVICE_TABLE(platform, max8998_battery_id);

View File

@@ -284,8 +284,8 @@ static int mm8013_probe(struct i2c_client *client)
}
static const struct i2c_device_id mm8013_id_table[] = {
{ "mm8013" },
{}
{ .name = "mm8013" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mm8013_id_table);

View File

@@ -841,15 +841,15 @@ static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = {
MODULE_DEVICE_TABLE(of, mt6360_charger_of_id);
static const struct platform_device_id mt6360_charger_id[] = {
{ "mt6360-chg", 0 },
{},
{ .name = "mt6360-chg" },
{ }
};
MODULE_DEVICE_TABLE(platform, mt6360_charger_id);
static struct platform_driver mt6360_charger_driver = {
.driver = {
.name = "mt6360-chg",
.of_match_table = of_match_ptr(mt6360_charger_of_id),
.of_match_table = mt6360_charger_of_id,
},
.probe = mt6360_charger_probe,
.id_table = mt6360_charger_id,

View File

@@ -622,7 +622,7 @@ static int pf1550_charger_probe(struct platform_device *pdev)
}
static const struct platform_device_id pf1550_charger_id[] = {
{ "pf1550-charger", },
{ .name = "pf1550-charger" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, pf1550_charger_id);

View File

@@ -292,18 +292,13 @@ static int power_supply_check_supplies(struct power_supply *psy)
if (cnt == 1)
return 0;
/* All supplies found, allocate char ** array for filling */
psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from),
/* All supplies found, allocate char * array for filling */
psy->supplied_from = devm_kcalloc(&psy->dev,
cnt - 1, sizeof(*psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from)
return -ENOMEM;
*psy->supplied_from = devm_kcalloc(&psy->dev,
cnt - 1, sizeof(**psy->supplied_from),
GFP_KERNEL);
if (!*psy->supplied_from)
return -ENOMEM;
return power_supply_populate_supplied_from(psy);
}
#else

View File

@@ -174,7 +174,7 @@ static int rt5033_battery_probe(struct i2c_client *client)
}
static const struct i2c_device_id rt5033_battery_id[] = {
{ "rt5033-battery", },
{ .name = "rt5033-battery" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);

View File

@@ -727,7 +727,7 @@ static int rt5033_charger_probe(struct platform_device *pdev)
}
static const struct platform_device_id rt5033_charger_id[] = {
{ "rt5033-charger", },
{ .name = "rt5033-charger" },
{ }
};
MODULE_DEVICE_TABLE(platform, rt5033_charger_id);

View File

@@ -1719,7 +1719,7 @@ static void rt9455_remove(struct i2c_client *client)
}
static const struct i2c_device_id rt9455_i2c_id_table[] = {
{ RT9455_DRIVER_NAME },
{ .name = RT9455_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table);

View File

@@ -0,0 +1,313 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Battery Charger Driver for Samsung S2M series PMICs.
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
* Copyright (c) 2026 Kaustabh Chakraborty <kauschluss@disroot.org>
* Copyright (c) 2026 Łukasz Lebiedziński <kernel@lvkasz.us>
*/
#include <linux/devm-helpers.h>
#include <linux/extcon.h>
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/s2mu005.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
struct s2m_chgr {
struct device *dev;
struct regmap *regmap;
struct power_supply *psy;
struct extcon_dev *extcon;
struct work_struct extcon_work;
struct notifier_block extcon_nb;
};
static int s2mu005_chgr_get_online(struct s2m_chgr *priv, int *value)
{
u32 val;
int ret;
ret = regmap_read(priv->regmap, S2MU005_REG_CHGR_STATUS0, &val);
if (ret) {
dev_err(priv->dev, "failed to read register (%d)\n", ret);
return ret;
}
*value = !!(val & S2MU005_CHGR_CHG);
return 0;
}
static void s2mu005_chgr_get_usb_type(struct s2m_chgr *priv, int *value)
{
if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_CDP) > 0)
*value = POWER_SUPPLY_USB_TYPE_CDP;
else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_SDP) > 0)
*value = POWER_SUPPLY_USB_TYPE_SDP;
else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_DCP) > 0)
*value = POWER_SUPPLY_USB_TYPE_DCP;
else
*value = POWER_SUPPLY_USB_TYPE_UNKNOWN;
}
static int s2mu005_chgr_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct s2m_chgr *priv = power_supply_get_drvdata(psy);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = s2mu005_chgr_get_online(priv, &val->intval);
if (ret)
return ret;
break;
case POWER_SUPPLY_PROP_USB_TYPE:
s2mu005_chgr_get_usb_type(priv, &val->intval);
break;
default:
return -EINVAL;
}
return 0;
}
static int s2mu005_chgr_mode_set_host(struct s2m_chgr *priv)
{
int ret;
/* set mode to OTG */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
S2MU005_CHGR_OP_MODE,
FIELD_PREP(S2MU005_CHGR_OP_MODE,
S2MU005_CHGR_OP_MODE_OTG));
if (ret) {
dev_err(priv->dev, "failed to set OTG mode (%d)\n", ret);
return ret;
}
/* set boost frequency to 2MHz */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL11,
S2MU005_CHGR_OSC_BOOST,
FIELD_PREP(S2MU005_CHGR_OSC_BOOST,
S2MU005_CHGR_OSC_BOOST_2MHZ));
if (ret) {
dev_err(priv->dev, "failed to set boost frequency (%d)\n", ret);
return ret;
}
/* set OTG current limit to 1.5 A */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4,
S2MU005_CHGR_OTG_OCP,
FIELD_PREP(S2MU005_CHGR_OTG_OCP,
S2MU005_CHGR_OTG_OCP_1P5A));
if (ret) {
dev_err(priv->dev, "failed to set OTG current limit (%d)\n", ret);
return ret;
}
/* VBUS switches are OFF when OTG over-current happens */
ret = regmap_set_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4,
S2MU005_CHGR_OTG_OCP_OFF);
if (ret) {
dev_err(priv->dev, "failed to set OTG OCP switch (%d)\n", ret);
return ret;
}
/* set OTG voltage to 5.1 V */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL5,
S2MU005_CHGR_VMID_BOOST,
FIELD_PREP(S2MU005_CHGR_VMID_BOOST,
S2MU005_CHGR_VMID_BOOST_5P1V));
if (ret) {
dev_err(priv->dev, "failed to set OTG voltage (%d)\n", ret);
return ret;
}
/* turn on OTG */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15,
S2MU005_CHGR_OTG_EN,
FIELD_PREP(S2MU005_CHGR_OTG_EN,
S2MU005_CHGR_OTG_EN_ON));
if (ret) {
dev_err(priv->dev, "failed to turn on OTG (%d)\n", ret);
return ret;
}
return 0;
}
static int s2mu005_chgr_mode_set_charger(struct s2m_chgr *priv)
{
int ret;
/* first reset to mode 0 */
ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
S2MU005_CHGR_OP_MODE);
if (ret) {
dev_err(priv->dev, "failed to reset opmode (%d)\n", ret);
return ret;
}
/* wait for the charger to settle before switching to charging mode */
msleep(50);
/* then set to charging mode */
ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
S2MU005_CHGR_OP_MODE,
FIELD_PREP(S2MU005_CHGR_OP_MODE,
S2MU005_CHGR_OP_MODE_CHG));
if (ret) {
dev_err(priv->dev, "failed to set opmode to charging (%d)\n", ret);
return ret;
}
return 0;
}
static int s2mu005_chgr_mode_unset(struct s2m_chgr *priv)
{
int ret;
/* turn off OTG */
ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15,
S2MU005_CHGR_OTG_EN);
if (ret) {
dev_err(priv->dev, "failed to turn off OTG (%d)\n", ret);
return ret;
}
/* reset operation mode */
ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
S2MU005_CHGR_OP_MODE);
if (ret) {
dev_err(priv->dev, "failed to reset opmode (%d)\n", ret);
return ret;
}
return 0;
}
static void s2mu005_chgr_extcon_work(struct work_struct *work)
{
struct s2m_chgr *priv = container_of(work, struct s2m_chgr, extcon_work);
if (extcon_get_state(priv->extcon, EXTCON_USB_HOST) > 0)
s2mu005_chgr_mode_set_host(priv);
else if (extcon_get_state(priv->extcon, EXTCON_USB) > 0)
s2mu005_chgr_mode_set_charger(priv);
else
s2mu005_chgr_mode_unset(priv);
power_supply_changed(priv->psy);
}
static const enum power_supply_property s2mu005_chgr_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_USB_TYPE,
};
static const struct power_supply_desc s2mu005_chgr_psy_desc = {
.name = "s2mu005-charger",
.type = POWER_SUPPLY_TYPE_USB,
.properties = s2mu005_chgr_properties,
.num_properties = ARRAY_SIZE(s2mu005_chgr_properties),
.get_property = s2mu005_chgr_get_property,
.usb_types = BIT(POWER_SUPPLY_USB_TYPE_CDP) |
BIT(POWER_SUPPLY_USB_TYPE_SDP) |
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN),
};
static int s2m_chgr_extcon_notifier(struct notifier_block *nb,
unsigned long event, void *param)
{
struct s2m_chgr *priv = container_of(nb, struct s2m_chgr, extcon_nb);
schedule_work(&priv->extcon_work);
return NOTIFY_OK;
}
static int s2m_chgr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);
struct s2m_chgr *priv;
struct device_node *extcon_node __free(device_node) = NULL;
struct power_supply_config psy_cfg = {};
const struct power_supply_desc *psy_desc;
work_func_t extcon_work_func;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->dev = dev;
priv->regmap = pmic_drvdata->regmap_pmic;
switch (platform_get_device_id(pdev)->driver_data) {
case S2MU005:
psy_desc = &s2mu005_chgr_psy_desc;
extcon_work_func = s2mu005_chgr_extcon_work;
break;
default:
return dev_err_probe(dev, -ENODEV,
"device type %d is not supported by driver\n",
pmic_drvdata->device_type);
}
/* MUIC is mandatory. If unavailable, request probe deferral */
extcon_node = of_get_child_by_name(dev->parent->of_node, "muic");
if (!extcon_node)
return dev_err_probe(dev, -ENODEV, "MUIC node required but not found\n");
priv->extcon = extcon_find_edev_by_node(extcon_node);
if (IS_ERR(priv->extcon))
return -EPROBE_DEFER;
psy_cfg.drv_data = priv;
psy_cfg.fwnode = dev_fwnode(dev->parent);
priv->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
if (IS_ERR(priv->psy))
return dev_err_probe(dev, PTR_ERR(priv->psy),
"failed to register power supply subsystem\n");
ret = devm_work_autocancel(dev, &priv->extcon_work, extcon_work_func);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize extcon work\n");
priv->extcon_nb.notifier_call = s2m_chgr_extcon_notifier;
ret = devm_extcon_register_notifier_all(dev, priv->extcon, &priv->extcon_nb);
if (ret)
return dev_err_probe(dev, ret, "failed to register extcon notifier\n");
return 0;
}
static const struct platform_device_id s2m_chgr_id_table[] = {
{ "s2mu005-charger", S2MU005 },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, s2m_chgr_id_table);
static struct platform_driver s2m_chgr_driver = {
.driver = {
.name = "s2m-charger",
},
.probe = s2m_chgr_probe,
.id_table = s2m_chgr_id_table,
};
module_platform_driver(s2m_chgr_driver);
MODULE_DESCRIPTION("Battery Charger Driver For Samsung S2M Series PMICs");
MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
MODULE_AUTHOR("Łukasz Lebiedziński <kernel@lvkasz.us>");
MODULE_LICENSE("GPL");

View File

@@ -1254,10 +1254,10 @@ static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
#endif
static const struct i2c_device_id sbs_id[] = {
{ "bq20z65", SBS_FLAGS_TI_BQ20ZX5 },
{ "bq20z75", SBS_FLAGS_TI_BQ20ZX5 },
{ "sbs-battery", 0 },
{}
{ .name = "bq20z65", .driver_data = SBS_FLAGS_TI_BQ20ZX5 },
{ .name = "bq20z75", .driver_data = SBS_FLAGS_TI_BQ20ZX5 },
{ .name = "sbs-battery", .driver_data = 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sbs_id);

View File

@@ -244,7 +244,7 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids);
#endif
static const struct i2c_device_id sbs_id[] = {
{ "sbs-charger" },
{ .name = "sbs-charger" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sbs_id);

View File

@@ -389,8 +389,8 @@ static int sbsm_probe(struct i2c_client *client)
}
static const struct i2c_device_id sbsm_ids[] = {
{ "sbs-manager" },
{ "ltc1760" },
{ .name = "sbs-manager" },
{ .name = "ltc1760" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sbsm_ids);

View File

@@ -1083,7 +1083,7 @@ static int smb347_get_charging_status(struct smb347_charger *smb,
} else {
/*
* in this case no charger error or termination
* occured but charging is not in progress!!!
* occurred but charging is not in progress!!!
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
@@ -1609,10 +1609,10 @@ static void smb347_shutdown(struct i2c_client *client)
}
static const struct i2c_device_id smb347_id[] = {
{ "smb345", SMB345 },
{ "smb347", SMB347 },
{ "smb358", SMB358 },
{ },
{ .name = "smb345", .driver_data = SMB345 },
{ .name = "smb347", .driver_data = SMB347 },
{ .name = "smb358", .driver_data = SMB358 },
{ }
};
MODULE_DEVICE_TABLE(i2c, smb347_id);

View File

@@ -584,7 +584,7 @@ static int stc3117_probe(struct i2c_client *client)
}
static const struct i2c_device_id stc3117_id[] = {
{ "stc3117", 0 },
{ .name = "stc3117", .driver_data = 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, stc3117_id);

View File

@@ -0,0 +1,389 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/devm-helpers.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/power_supply.h>
#include <linux/types.h>
/* Register Addresses (B=byte; W=word; S=string) */
#define REGB_STATUS 0x02
#define REGW_VOLTAGE_NOW 0x20
#define REGW_CURRENT_NOW 0x24
#define REGW_CAPACITY 0x28
#define REGW_CHARGE_NOW 0x2a
#define REGW_CHARGE_FULL 0x2c
#define REGW_CYCLE_COUNT 0x3a
#define REGW_CHARGE_FULL_DESIGN 0x3c
#define REGW_VOLTAGE_MAX_DESIGN 0x3e
#define REGW_SERIAL_NUMBER 0x44
#define REGS_MANUFACTURER 0x46
#define REGS_MODEL_NAME 0x52
#define REGS_TECHNOLOGY 0x5a
#define REGB_ONLINE 0x67
struct srt_ec_device {
struct i2c_client *client;
struct power_supply *bat;
struct power_supply *psy;
struct gpio_desc *enable_gpiod;
struct delayed_work poll_work;
unsigned int technology;
unsigned int capacity;
const char *serial;
char manufacturer[13];
char model_name[10];
};
static const enum power_supply_property srt_bat_power_supply_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
static const enum power_supply_property srt_psy_power_supply_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
};
static int srt_bat_get_value(struct i2c_client *client, int reg, int *val)
{
int ret;
switch (reg) {
case REGW_CHARGE_NOW:
case REGW_CHARGE_FULL_DESIGN:
case REGW_CHARGE_FULL:
case REGW_VOLTAGE_MAX_DESIGN:
case REGW_VOLTAGE_NOW:
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0)
return ret;
*val = ret * 1000;
break;
case REGW_CURRENT_NOW:
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0)
return ret;
*val = (s16)ret * 1000;
break;
case REGW_CAPACITY:
case REGW_CYCLE_COUNT:
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0)
return ret;
*val = ret;
break;
case REGB_STATUS:
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
return ret;
if (ret & BIT(0))
*val = POWER_SUPPLY_STATUS_CHARGING;
else
*val = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case REGB_ONLINE:
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
return ret;
*val = (ret & BIT(1)) >> 1;
break;
default:
return -EINVAL;
}
return 0;
}
static int srt_bat_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct srt_ec_device *srt = power_supply_get_drvdata(psy);
struct i2c_client *client = srt->client;
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = srt->manufacturer;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = srt->model_name;
break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
val->strval = srt->serial;
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = srt_bat_get_value(client, REGW_CAPACITY, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
ret = srt_bat_get_value(client, REGW_CHARGE_NOW, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
ret = srt_bat_get_value(client, REGW_CHARGE_FULL, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
ret = srt_bat_get_value(client, REGW_CHARGE_FULL_DESIGN,
&val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = srt_bat_get_value(client, REGW_CURRENT_NOW, &val->intval);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
ret = srt_bat_get_value(client, REGW_CYCLE_COUNT, &val->intval);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_ONLINE:
ret = srt_bat_get_value(client, REGB_ONLINE, &val->intval);
break;
case POWER_SUPPLY_PROP_STATUS:
if (srt->capacity < 100)
ret = srt_bat_get_value(client, REGB_STATUS, &val->intval);
else
val->intval = POWER_SUPPLY_STATUS_FULL;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = srt->technology;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
ret = srt_bat_get_value(client, REGW_VOLTAGE_MAX_DESIGN,
&val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = srt_bat_get_value(client, REGW_VOLTAGE_NOW, &val->intval);
break;
default:
return -EINVAL;
}
return ret;
}
static int srt_psy_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct srt_ec_device *srt = power_supply_get_drvdata(psy);
struct i2c_client *client = srt->client;
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_PRESENT:
ret = i2c_smbus_read_byte_data(client, REGB_ONLINE);
if (ret < 0)
return ret;
val->intval = ret & BIT(0);
break;
default:
return -EINVAL;
}
return 0;
}
static void srt_bat_poll_work(struct work_struct *work)
{
struct srt_ec_device *srt =
container_of(work, struct srt_ec_device, poll_work.work);
int ret, capacity;
ret = srt_bat_get_value(srt->client, REGW_CAPACITY, &capacity);
if (!ret && capacity != srt->capacity) {
srt->capacity = capacity;
power_supply_changed(srt->bat);
}
/* continuously send uevent notification */
schedule_delayed_work(&srt->poll_work, 30 * HZ);
}
static irqreturn_t srt_psy_detect_irq(int irq, void *dev_id)
{
struct srt_ec_device *srt = dev_id;
power_supply_changed(srt->psy);
return IRQ_HANDLED;
}
static const struct power_supply_desc srt_bat_power_supply_desc = {
.name = "surface-rt-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = srt_bat_power_supply_props,
.num_properties = ARRAY_SIZE(srt_bat_power_supply_props),
.get_property = srt_bat_power_supply_get_property,
.external_power_changed = power_supply_changed,
};
static const struct power_supply_desc srt_psy_power_supply_desc = {
.name = "surface-rt-ac-adapter",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = srt_psy_power_supply_props,
.num_properties = ARRAY_SIZE(srt_psy_power_supply_props),
.get_property = srt_psy_power_supply_get_property,
};
static char *battery_supplied_to[] = { "surface-rt-battery" };
static int srt_ec_probe(struct i2c_client *client)
{
struct power_supply_config bat_cfg = {};
struct power_supply_config psy_cfg = {};
struct device *dev = &client->dev;
struct srt_ec_device *srt;
char str_buf[4];
int ret;
srt = devm_kzalloc(dev, sizeof(*srt), GFP_KERNEL);
if (!srt)
return -ENOMEM;
i2c_set_clientdata(client, srt);
srt->client = client;
srt->enable_gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
if (IS_ERR(srt->enable_gpiod))
return dev_err_probe(dev, PTR_ERR(srt->enable_gpiod),
"failed to get enable gpio\n");
/* wait till EC is ready */
usleep_range(1000, 1500);
ret = i2c_smbus_read_word_data(client, REGW_SERIAL_NUMBER);
if (ret < 0)
return ret;
srt->serial = devm_kasprintf(dev, GFP_KERNEL, "%04x", ret);
if (!srt->serial)
return -ENOMEM;
ret = i2c_smbus_read_i2c_block_data(client, REGS_MANUFACTURER,
sizeof(srt->manufacturer) - 1,
srt->manufacturer);
if (ret < 0)
return ret;
ret = i2c_smbus_read_i2c_block_data(client, REGS_MODEL_NAME,
sizeof(srt->model_name) - 1,
srt->model_name);
if (ret < 0)
return ret;
ret = i2c_smbus_read_i2c_block_data(client, REGS_TECHNOLOGY,
sizeof(str_buf), str_buf);
if (ret < 0)
return ret;
if (!strncmp(str_buf, "LION", 4))
srt->technology = POWER_SUPPLY_TECHNOLOGY_LION;
else
srt->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
bat_cfg.drv_data = srt;
bat_cfg.fwnode = dev_fwnode(dev);
srt->bat = devm_power_supply_register(dev, &srt_bat_power_supply_desc,
&bat_cfg);
if (IS_ERR(srt->bat))
return dev_err_probe(dev, PTR_ERR(srt->bat),
"failed to register battery power supply\n");
psy_cfg.drv_data = srt;
psy_cfg.fwnode = dev_fwnode(dev);
psy_cfg.supplied_to = battery_supplied_to;
psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
srt->psy = devm_power_supply_register(dev, &srt_psy_power_supply_desc,
&psy_cfg);
if (IS_ERR(srt->psy))
return dev_err_probe(dev, PTR_ERR(srt->psy),
"failed to register AC power supply\n");
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");
ret = devm_delayed_work_autocancel(dev, &srt->poll_work, srt_bat_poll_work);
if (ret < 0)
return ret;
schedule_delayed_work(&srt->poll_work, HZ);
return 0;
}
static int srt_ec_suspend(struct device *dev)
{
struct srt_ec_device *srt = dev_get_drvdata(dev);
cancel_delayed_work_sync(&srt->poll_work);
return 0;
}
static int srt_ec_resume(struct device *dev)
{
struct srt_ec_device *srt = dev_get_drvdata(dev);
schedule_delayed_work(&srt->poll_work, HZ);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(srt_ec_pm_ops, srt_ec_suspend, srt_ec_resume);
static const struct of_device_id srt_ec_of_match[] = {
{ .compatible = "microsoft,surface-rt-ec" },
{ }
};
MODULE_DEVICE_TABLE(of, srt_ec_of_match);
static struct i2c_driver srt_ec_driver = {
.driver = {
.name = "surface-rt-ec",
.of_match_table = srt_ec_of_match,
.pm = &srt_ec_pm_ops,
},
.probe = srt_ec_probe,
};
module_i2c_driver(srt_ec_driver);
MODULE_AUTHOR("Jonas Schwöbel <jonasschwoebel@yahoo.de>");
MODULE_DESCRIPTION("Surface RT Embedded Controller driver");
MODULE_LICENSE("GPL");

View File

@@ -195,7 +195,7 @@ static SIMPLE_DEV_PM_OPS(ug3105_pm_ops, ug3105_suspend,
ug3105_resume);
static const struct i2c_device_id ug3105_id[] = {
{ "ug3105" },
{ .name = "ug3105" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ug3105_id);

View File

@@ -24,6 +24,8 @@
#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
#define MAX17055_MODELCFG_REFRESH_BIT BIT(15)
enum max17042_register {
MAX17042_STATUS = 0x00,
MAX17042_VALRT_Th = 0x01,
@@ -198,16 +200,6 @@ enum max170xx_chip_type {
MAXIM_DEVICE_TYPE_NUM
};
/*
* used for setting a register to a desired value
* addr : address for a register
* data : setting value for the register
*/
struct max17042_reg_data {
u8 addr;
u16 data;
};
struct max17042_config_data {
/* External current sense resistor value in milli-ohms */
u32 cur_sense_val;
@@ -229,6 +221,7 @@ struct max17042_config_data {
u16 full_soc_thresh; /* 0x13 */
u16 design_cap; /* 0x18 */
u16 ichgt_term; /* 0x1E */
u16 model_cfg; /* 0xDB */
/* MG3 config */
u16 at_rate; /* 0x04 */
@@ -265,23 +258,4 @@ struct max17042_config_data {
u16 cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE];
} __packed;
struct max17042_platform_data {
struct max17042_reg_data *init_data;
struct max17042_config_data *config_data;
int num_init_data; /* Number of enties in init_data array */
bool enable_current_sense;
bool enable_por_init; /* Use POR init from Maxim appnote */
/*
* R_sns in micro-ohms.
* default 10000 (if r_sns = 0) as it is the recommended value by
* the datasheet although it can be changed by board designers.
*/
unsigned int r_sns;
int vmin; /* in millivolts */
int vmax; /* in millivolts */
int temp_min; /* in tenths of degree Celsius */
int temp_max; /* in tenths of degree Celsius */
};
#endif /* __MAX17042_BATTERY_H_ */