mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
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:
12
Documentation/ABI/testing/sysfs-class-power-bd71828
Normal file
12
Documentation/ABI/testing/sysfs-class-power-bd71828
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)<c4015_chip_info },
|
||||
{ "ltc4162-f", (kernel_ulong_t)<c4162f_chip_info },
|
||||
{ "ltc4162-l", (kernel_ulong_t)<c4162l_chip_info },
|
||||
{ "ltc4162-s", (kernel_ulong_t)<c4162s_chip_info },
|
||||
{ .name = "ltc4015", .driver_data = (kernel_ulong_t)<c4015_chip_info },
|
||||
{ .name = "ltc4162-f", .driver_data = (kernel_ulong_t)<c4162f_chip_info },
|
||||
{ .name = "ltc4162-l", .driver_data = (kernel_ulong_t)<c4162l_chip_info },
|
||||
{ .name = "ltc4162-s", .driver_data = (kernel_ulong_t)<c4162s_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
313
drivers/power/supply/s2m-charger.c
Normal file
313
drivers/power/supply/s2m-charger.c
Normal 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");
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
389
drivers/power/supply/surface-rt-ec.c
Normal file
389
drivers/power/supply/surface-rt-ec.c
Normal 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");
|
||||
@@ -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);
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user