Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git

This commit is contained in:
Stephen Rothwell 2025-01-14 13:22:49 +11:00
commit 3be3276b26
45 changed files with 2466 additions and 627 deletions

View File

@ -407,10 +407,30 @@ Description:
Access: Read, Write
Reading this returns the current active value, e.g. 'Standard'.
Check charge_types to get the values supported by the battery.
Valid values:
"Unknown", "N/A", "Trickle", "Fast", "Standard",
"Adaptive", "Custom", "Long Life", "Bypass"
What: /sys/class/power_supply/<supply_name>/charge_types
Date: December 2024
Contact: linux-pm@vger.kernel.org
Description:
Identical to charge_type but reading returns a list of supported
charge-types with the currently active type surrounded by square
brackets, e.g.: "Fast [Standard] Long_Life".
power_supply class devices may support both charge_type and
charge_types for backward compatibility. In this case both will
always have the same active value and the active value can be
changed by writing either property.
Note charge-types which contain a space such as "Long Life" will
have the space replaced by a '_' resulting in e.g. "Long_Life".
When writing charge-types both variants are accepted.
What: /sys/class/power_supply/<supply_name>/charge_term_current
Date: July 2014
Contact: linux-pm@vger.kernel.org
@ -793,3 +813,12 @@ Description:
Access: Read
Valid values: 1-31
What: /sys/class/power_supply/<supply_name>/extensions/<extension_name>
Date: March 2025
Contact: linux-pm@vger.kernel.org
Description:
Reports the extensions registered to the power supply.
Each entry is a link to the device which registered the extension.
Access: Read

View File

@ -22,6 +22,9 @@ properties:
- enum:
- atmel,sama5d2-shdwc
- microchip,sam9x60-shdwc
- items:
- const: microchip,sam9x7-shdwc
- const: microchip,sam9x60-shdwc
reg:
maxItems: 1

View File

@ -21,6 +21,7 @@ properties:
- ti,bq24192i
- ti,bq24196
- ti,bq24296
- ti,bq24297
reg:
maxItems: 1

View File

@ -58,6 +58,10 @@ properties:
charge-current-limit-gpios property. Bit 1 second to last
GPIO and so on.
charge-current-limit-default-microamp:
description: Default charge current limit. Must be listed in
charge-current-limit-mapping.
required:
- compatible
@ -72,6 +76,7 @@ anyOf:
dependencies:
charge-current-limit-gpios: [ charge-current-limit-mapping ]
charge-current-limit-mapping: [ charge-current-limit-gpios ]
charge-current-limit-default-microamp: [charge-current-limit-mapping]
additionalProperties: false
@ -91,4 +96,5 @@ examples:
charge-current-limit-mapping = <2500000 0x00>, // 2.5 A => both GPIOs low
<700000 0x01>, // 700 mA => GPIO A.12 high
<0 0x02>; // 0 mA => GPIO A.11 high
charge-current-limit-default-microamp = <700000>;
};

View File

@ -17,12 +17,18 @@ description: |
panels, etc., and a rechargeable Lithium-Ion/Polymer battery.
Specifications about the charger can be found at:
https://www.analog.com/en/products/ltc4162-l.html
https://www.analog.com/en/products/ltc4162-f.html
https://www.analog.com/en/products/ltc4162-s.html
https://www.analog.com/en/products/ltc4015.html
properties:
compatible:
enum:
- lltc,ltc4015
- lltc,ltc4162-f
- lltc,ltc4162-l
- lltc,ltc4162-s
reg:
maxItems: 1

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/st,stc3117.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics STC3117 Fuel Gauge Unit Power Supply
maintainers:
- Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
- Bhavin Sharma <bhavin.sharma@siliconsignals.io>
description: |
The STC3117 includes the STMicroelectronics OptimGauge algorithm.
It provides accurate battery state-of-charge (SOC) monitoring, tracks
battery parameter changes with operation conditions, temperature,
and aging, and allows the application to get a battery state-of-health
(SOH) indication.
An alarm output signals low SOC or low voltage conditions and also
indicates fault conditions like a missing or swapped battery.
Datasheet is available at
https://www.st.com/resource/en/datasheet/stc3117.pdf
allOf:
- $ref: power-supply.yaml#
properties:
compatible:
enum:
- st,stc3117
reg:
maxItems: 1
monitored-battery:
description: |
The fuel gauge uses the following battery properties:
- charge-full-design-microamp-hours
- voltage-min-design-microvolt
- voltage-max-design-microvolt
shunt-resistor-micro-ohms:
description: Current sense resistor
interrupts:
maxItems: 1
required:
- compatible
- reg
- monitored-battery
- shunt-resistor-micro-ohms
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
battery@70 {
compatible = "st,stc3117";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <31 IRQ_TYPE_LEVEL_LOW>;
monitored-battery = <&bat>;
shunt-resistor-micro-ohms = <10000>;
};
};

View File

@ -147,6 +147,8 @@ properties:
- injoinic,ip5207
# Injoinic IP5209 2.4A Power Bank IC with I2C
- injoinic,ip5209
# Injoinic IP5306 2.1A Power Bank IC with I2C option
- injoinic,ip5306
# Inspur Power System power supply unit version 1
- inspur,ipsps1
# Intel common redudant power supply crps185

View File

@ -22264,6 +22264,14 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml
F: drivers/media/i2c/st-mipid02.c
ST STC3117 FUEL GAUGE DRIVER
M: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
M: Bhavin Sharma <bhavin.sharma@siliconsignals.io>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/power/supply/st,stc3117.yaml
F: drivers/power/supply/stc3117_fuel_gauge.c
ST STM32 FIREWALL
M: Gatien Chevallier <gatien.chevallier@foss.st.com>
S: Maintained

View File

@ -103,15 +103,15 @@ static bool mute_led_registered;
struct battery_mode_info {
int token;
const char *label;
enum power_supply_charge_type charge_type;
};
static const struct battery_mode_info battery_modes[] = {
{ BAT_PRI_AC_MODE_TOKEN, "Trickle" },
{ BAT_EXPRESS_MODE_TOKEN, "Fast" },
{ BAT_STANDARD_MODE_TOKEN, "Standard" },
{ BAT_ADAPTIVE_MODE_TOKEN, "Adaptive" },
{ BAT_CUSTOM_MODE_TOKEN, "Custom" },
{ BAT_PRI_AC_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_TRICKLE },
{ BAT_EXPRESS_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_FAST },
{ BAT_STANDARD_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_STANDARD },
{ BAT_ADAPTIVE_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE },
{ BAT_CUSTOM_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_CUSTOM },
};
static u32 battery_supported_modes;
@ -2261,46 +2261,42 @@ static ssize_t charge_types_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t count = 0;
enum power_supply_charge_type charge_type;
int i;
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
bool active;
charge_type = battery_modes[i].charge_type;
if (!(battery_supported_modes & BIT(i)))
if (!(battery_supported_modes & BIT(charge_type)))
continue;
active = dell_battery_mode_is_active(battery_modes[i].token);
count += sysfs_emit_at(buf, count, active ? "[%s] " : "%s ",
battery_modes[i].label);
if (!dell_battery_mode_is_active(battery_modes[i].token))
continue;
return power_supply_charge_types_show(dev, battery_supported_modes,
charge_type, buf);
}
/* convert the last space to a newline */
if (count > 0)
count--;
count += sysfs_emit_at(buf, count, "\n");
return count;
/* No active mode found */
return -EIO;
}
static ssize_t charge_types_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
bool matched = false;
int err, i;
int charge_type, err, i;
charge_type = power_supply_charge_types_parse(battery_supported_modes, buf);
if (charge_type < 0)
return charge_type;
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
if (!(battery_supported_modes & BIT(i)))
continue;
if (sysfs_streq(battery_modes[i].label, buf)) {
matched = true;
if (battery_modes[i].charge_type == charge_type)
break;
}
}
if (!matched)
return -EINVAL;
if (i == ARRAY_SIZE(battery_modes))
return -ENOENT;
err = dell_battery_set_mode(battery_modes[i].token);
if (err)
@ -2430,7 +2426,7 @@ static u32 __init battery_get_supported_modes(void)
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
if (dell_smbios_find_token(battery_modes[i].token))
modes |= BIT(i);
modes |= BIT(battery_modes[i].charge_type);
}
return modes;

View File

@ -26,7 +26,7 @@ config POWER_RESET_AT91_POWEROFF
config POWER_RESET_AT91_RESET
tristate "Atmel AT91 reset driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5
default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5
help
This driver supports restart for Atmel AT91SAM9 and SAMA5
SoCs
@ -34,7 +34,7 @@ config POWER_RESET_AT91_RESET
config POWER_RESET_AT91_SAMA5D2_SHDWC
tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver"
depends on ARCH_AT91
default SOC_SAM9X60 || SOC_SAMA5
default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5
help
This driver supports the alternate shutdown controller for some Atmel
SAMA5 SoCs. It is present for example on SAMA5D2 SoC.

View File

@ -57,8 +57,6 @@ static int as3722_poweroff_probe(struct platform_device *pdev)
SYS_OFF_PRIO_DEFAULT,
as3722_pm_power_off,
as3722_poweroff);
return 0;
}
static struct platform_driver as3722_poweroff_driver = {

View File

@ -326,6 +326,7 @@ static const struct of_device_id at91_pmc_ids[] = {
{ .compatible = "atmel,sama5d2-pmc" },
{ .compatible = "microchip,sam9x60-pmc" },
{ .compatible = "microchip,sama7g5-pmc" },
{ .compatible = "microchip,sam9x7-pmc" },
{ /* Sentinel. */ }
};

View File

@ -44,7 +44,13 @@ static int gpio_poweroff_do_poweroff(struct sys_off_data *data)
/* give it some time */
mdelay(gpio_poweroff->timeout_ms);
WARN_ON(1);
/*
* If code reaches this point, it means that gpio-poweroff has failed
* to actually power off the system.
* Warn the user that the attempt to poweroff via gpio-poweroff
* has gone wrong.
*/
WARN(1, "Failed to poweroff via gpio-poweroff mechanism\n");
return NOTIFY_DONE;
}

View File

@ -918,6 +918,15 @@ config FUEL_GAUGE_SC27XX
Say Y here to enable support for fuel gauge with SC27XX
PMIC chips.
config FUEL_GAUGE_STC3117
tristate "STMicroelectronics STC3117 fuel gauge driver"
depends on CRC8
depends on I2C
select REGMAP_I2C
help
Say Y here to enable support for fuel gauge with STC3117
chip.
config CHARGER_UCS1002
tristate "Microchip UCS1002 USB Port Power Controller"
depends on I2C

View File

@ -108,6 +108,7 @@ obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
obj-$(CONFIG_FUEL_GAUGE_STC3117) += stc3117_fuel_gauge.o
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o

View File

@ -540,10 +540,9 @@ static int ab8500_btemp_get_property(struct power_supply *psy,
return 0;
}
static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
static int ab8500_btemp_get_ext_psy_data(struct power_supply *ext, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct ab8500_btemp *di;
union power_supply_propval ret;
@ -617,7 +616,7 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
*/
static void ab8500_btemp_external_power_changed(struct power_supply *psy)
{
power_supply_for_each_device(psy, ab8500_btemp_get_ext_psy_data);
power_supply_for_each_psy(psy, ab8500_btemp_get_ext_psy_data);
}
/* ab8500 btemp driver interrupts and their respective isr */

View File

@ -844,10 +844,9 @@ static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
}
}
static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
static int ab8500_chargalg_get_ext_psy_data(struct power_supply *ext, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct ab8500_chargalg *di;
union power_supply_propval ret;
@ -1231,7 +1230,7 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
int ret;
/* Collect data from all power_supply class devices */
power_supply_for_each_device(di->chargalg_psy, ab8500_chargalg_get_ext_psy_data);
power_supply_for_each_psy(di->chargalg_psy, ab8500_chargalg_get_ext_psy_data);
ab8500_chargalg_end_of_charge(di);
ab8500_chargalg_check_temp(di);

View File

@ -1894,10 +1894,9 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
return ret;
}
static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
static int ab8500_charger_get_ext_psy_data(struct power_supply *ext, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct ab8500_charger *di;
union power_supply_propval ret;
@ -1961,7 +1960,7 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work)
struct ab8500_charger *di = container_of(work,
struct ab8500_charger, check_vbat_work.work);
power_supply_for_each_device(&di->usb_chg, ab8500_charger_get_ext_psy_data);
power_supply_for_each_psy(&di->usb_chg, ab8500_charger_get_ext_psy_data);
/* First run old_vbat is 0. */
if (di->old_vbat == 0)

View File

@ -2174,10 +2174,9 @@ static int ab8500_fg_get_property(struct power_supply *psy,
return 0;
}
static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
static int ab8500_fg_get_ext_psy_data(struct power_supply *ext, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct ab8500_fg *di;
struct power_supply_battery_info *bi;
@ -2402,7 +2401,7 @@ out:
*/
static void ab8500_fg_external_power_changed(struct power_supply *psy)
{
power_supply_for_each_device(psy, ab8500_fg_get_ext_psy_data);
power_supply_for_each_psy(psy, ab8500_fg_get_ext_psy_data);
}
/**
@ -2575,7 +2574,7 @@ static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2598,7 +2597,7 @@ static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
if (kstrtoint(buf, 10, &reg_value))
@ -2625,7 +2624,7 @@ static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2649,7 +2648,7 @@ static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
if (kstrtoint(buf, 10, &reg_value))
@ -2676,7 +2675,7 @@ static ssize_t ab8505_powercut_restart_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2699,7 +2698,7 @@ static ssize_t ab8505_powercut_restart_write(struct device *dev,
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
if (kstrtoint(buf, 10, &reg_value))
@ -2727,7 +2726,7 @@ static ssize_t ab8505_powercut_timer_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2750,7 +2749,7 @@ static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2773,7 +2772,7 @@ static ssize_t ab8505_powercut_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2794,7 +2793,7 @@ static ssize_t ab8505_powercut_write(struct device *dev,
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
if (kstrtoint(buf, 10, &reg_value))
@ -2822,7 +2821,7 @@ static ssize_t ab8505_powercut_flag_read(struct device *dev,
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2845,7 +2844,7 @@ static ssize_t ab8505_powercut_debounce_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
@ -2868,7 +2867,7 @@ static ssize_t ab8505_powercut_debounce_write(struct device *dev,
{
int ret;
int reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
if (kstrtoint(buf, 10, &reg_value))
@ -2895,7 +2894,7 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
{
int ret;
u8 reg_value;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct ab8500_fg *di = power_supply_get_drvdata(psy);
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,

View File

@ -42,11 +42,11 @@ struct find_bat_param {
int max_energy;
};
static int __find_main_battery(struct device *dev, void *data)
static int __find_main_battery(struct power_supply *psy, void *data)
{
struct find_bat_param *bp = (struct find_bat_param *)data;
bp->bat = dev_get_drvdata(dev);
bp->bat = psy;
if (bp->bat->desc->use_for_apm) {
/* nice, we explicitly asked to report this battery. */
@ -79,7 +79,7 @@ static void find_main_battery(void)
main_battery = NULL;
bp.main = main_battery;
error = power_supply_for_each_device(&bp, __find_main_battery);
error = power_supply_for_each_psy(&bp, __find_main_battery);
if (error) {
main_battery = bp.main;
return;

View File

@ -171,6 +171,7 @@ struct bq2415x_device {
char *name;
int autotimer; /* 1 - if driver automatically reset timer, 0 - not */
int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */
int charge_status;
int id;
};
@ -835,11 +836,13 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
if (!bq2415x_update_reported_mode(bq, prop.intval))
return NOTIFY_OK;
power_supply_changed(bq->charger);
/* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1)
return NOTIFY_OK;
schedule_delayed_work(&bq->work, 0);
mod_delayed_work(system_wq, &bq->work, 0);
return NOTIFY_OK;
}
@ -889,12 +892,19 @@ static void bq2415x_timer_work(struct work_struct *work)
int ret;
int error;
int boost;
int charge;
if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode");
bq2415x_set_mode(bq, bq->reported_mode);
}
charge = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
if (bq->charge_status != charge) {
power_supply_changed(bq->charger);
bq->charge_status = charge;
}
if (!bq->autotimer)
return;
@ -1050,7 +1060,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
enum bq2415x_command command;
int ret;
@ -1083,7 +1093,7 @@ static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
int ret = 0;
@ -1104,7 +1114,7 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
if (bq->timer_error)
@ -1128,7 +1138,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
enum bq2415x_mode mode;
int ret = 0;
@ -1180,7 +1190,7 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
ssize_t ret = 0;
@ -1217,7 +1227,7 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
if (bq->automode < 0)
@ -1245,7 +1255,7 @@ static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
ssize_t ret = 0;
unsigned int reg;
@ -1280,7 +1290,7 @@ static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
ssize_t ret = 0;
@ -1298,7 +1308,7 @@ static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
long val;
int ret;
@ -1329,7 +1339,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
int ret;
@ -1357,7 +1367,7 @@ static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
enum bq2415x_command command;
long val;
@ -1392,7 +1402,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq2415x_device *bq = power_supply_get_drvdata(psy);
enum bq2415x_command command;
int ret;

View File

@ -152,6 +152,7 @@
#define BQ24296_REG_VPRS_PN_MASK (BIT(7) | BIT(6) | BIT(5))
#define BQ24296_REG_VPRS_PN_SHIFT 5
#define BQ24296_REG_VPRS_PN_24296 0x1
#define BQ24296_REG_VPRS_PN_24297 0x3
#define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2)
#define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2
#define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0))
@ -208,6 +209,7 @@ enum bq24190_chip {
BQ24192i,
BQ24196,
BQ24296,
BQ24297,
};
/*
@ -422,7 +424,7 @@ static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = {
BQ24190_SYSFS_FIELD_RO(watchdog, CTTC, WATCHDOG),
BQ24190_SYSFS_FIELD_RW(en_timer, CTTC, EN_TIMER),
BQ24190_SYSFS_FIELD_RW(chg_timer, CTTC, CHG_TIMER),
BQ24190_SYSFS_FIELD_RW(jeta_iset, CTTC, JEITA_ISET),
BQ24190_SYSFS_FIELD_RW(jeita_iset, CTTC, JEITA_ISET),
BQ24190_SYSFS_FIELD_RW(bat_comp, ICTRC, BAT_COMP),
BQ24190_SYSFS_FIELD_RW(vclamp, ICTRC, VCLAMP),
BQ24190_SYSFS_FIELD_RW(treg, ICTRC, TREG),
@ -480,7 +482,7 @@ static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup(
static ssize_t bq24190_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
struct bq24190_sysfs_field_info *info;
ssize_t count;
@ -510,7 +512,7 @@ static ssize_t bq24190_sysfs_show(struct device *dev,
static ssize_t bq24190_sysfs_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
struct bq24190_sysfs_field_info *info;
int ret;
@ -1319,6 +1321,7 @@ static int bq24190_charger_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_TYPE:
case POWER_SUPPLY_PROP_CHARGE_TYPES:
ret = bq24190_charger_get_charge_type(bdi, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
@ -1399,6 +1402,7 @@ static int bq24190_charger_set_property(struct power_supply *psy,
ret = bq24190_charger_set_temp_alert_max(bdi, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
case POWER_SUPPLY_PROP_CHARGE_TYPES:
ret = bq24190_charger_set_charge_type(bdi, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
@ -1427,6 +1431,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
case POWER_SUPPLY_PROP_CHARGE_TYPE:
case POWER_SUPPLY_PROP_CHARGE_TYPES:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
@ -1475,6 +1480,7 @@ static void bq24190_charger_external_power_changed(struct power_supply *psy)
static enum power_supply_property bq24190_charger_properties[] = {
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CHARGE_TYPES,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@ -1504,6 +1510,9 @@ static const struct power_supply_desc bq24190_charger_desc = {
.set_property = bq24190_charger_set_property,
.property_is_writeable = bq24190_charger_property_is_writeable,
.external_power_changed = bq24190_charger_external_power_changed,
.charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_NONE) |
BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) |
BIT(POWER_SUPPLY_CHARGE_TYPE_FAST),
};
/* Battery power supply property routines */
@ -1897,6 +1906,7 @@ static int bq24296_check_chip(struct bq24190_dev_info *bdi)
switch (v) {
case BQ24296_REG_VPRS_PN_24296:
case BQ24296_REG_VPRS_PN_24297:
break;
default:
dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v);
@ -2026,6 +2036,17 @@ static const struct bq24190_chip_info bq24190_chip_info_tbl[] = {
.ichg_array_size = BQ24296_CCC_ICHG_VALUES_LEN,
#ifdef CONFIG_REGULATOR
.vbus_desc = &bq24296_vbus_desc,
#endif
.check_chip = bq24296_check_chip,
.set_chg_config = bq24296_battery_set_chg_config,
.ntc_fault_mask = BQ24296_REG_F_NTC_FAULT_MASK,
.get_ntc_status = bq24296_charger_get_ntc_status,
.set_otg_vbus = bq24296_set_otg_vbus,
},
[BQ24297] = {
.ichg_array_size = BQ24296_CCC_ICHG_VALUES_LEN,
#ifdef CONFIG_REGULATOR
.vbus_desc = &bq24296_vbus_desc,
#endif
.check_chip = bq24296_check_chip,
.set_chg_config = bq24296_battery_set_chg_config,
@ -2289,6 +2310,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
{ "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] },
{ "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] },
{ },
};
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
@ -2299,6 +2321,7 @@ static const struct of_device_id bq24190_of_match[] = {
{ .compatible = "ti,bq24192i", .data = &bq24190_chip_info_tbl[BQ24192i] },
{ .compatible = "ti,bq24196", .data = &bq24190_chip_info_tbl[BQ24196] },
{ .compatible = "ti,bq24296", .data = &bq24190_chip_info_tbl[BQ24296] },
{ .compatible = "ti,bq24297", .data = &bq24190_chip_info_tbl[BQ24297] },
{ },
};
MODULE_DEVICE_TABLE(of, bq24190_of_match);

View File

@ -759,7 +759,7 @@ static ssize_t bq24257_show_ovp_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return sysfs_emit(buf, "%u\n", bq24257_vovp_map[bq->init_data.vovp]);
@ -769,7 +769,7 @@ static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return sysfs_emit(buf, "%u\n", bq24257_vindpm_map[bq->init_data.vindpm]);
@ -779,7 +779,7 @@ static ssize_t bq24257_sysfs_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
int ret;
@ -801,7 +801,7 @@ static ssize_t bq24257_sysfs_set_enable(struct device *dev,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
long val;
int ret;

View File

@ -123,6 +123,7 @@ enum bq27xxx_reg_index {
BQ27XXX_DM_BLOCK, /* Data Block */
BQ27XXX_DM_DATA, /* Block Data */
BQ27XXX_DM_CKSUM, /* Block Data Checksum */
BQ27XXX_REG_SEDVF, /* End-of-discharge Voltage */
BQ27XXX_REG_MAX, /* sentinel */
};
@ -159,6 +160,7 @@ static u8
[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
[BQ27XXX_REG_SEDVF] = 0x77,
},
bq27010_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
@ -184,6 +186,7 @@ static u8
[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
[BQ27XXX_REG_SEDVF] = 0x77,
},
bq2750x_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
@ -579,6 +582,7 @@ static enum power_supply_property bq27000_props[] = {
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
};
static enum power_supply_property bq27010_props[] = {
@ -599,6 +603,7 @@ static enum power_supply_property bq27010_props[] = {
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
};
#define bq2750x_props bq27510g3_props
@ -2039,6 +2044,36 @@ static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
return 0;
}
/*
* Return the design minimum battery Voltage in microvolts
* Or < 0 if something fails.
*/
static int bq27xxx_battery_read_dmin_volt(struct bq27xxx_device_info *di,
union power_supply_propval *val)
{
int volt;
/* We only have to read design minimum voltage once */
if (di->voltage_min_design > 0) {
val->intval = di->voltage_min_design;
return 0;
}
volt = bq27xxx_read(di, BQ27XXX_REG_SEDVF, true);
if (volt < 0) {
dev_err(di->dev, "error reading design min voltage\n");
return volt;
}
/* SEDVF = Design EDVF / 8 - 256 */
val->intval = volt * 8000 + 2048000;
/* Save for later reads */
di->voltage_min_design = val->intval;
return 0;
}
static int bq27xxx_simple_value(int value,
union power_supply_propval *val)
{
@ -2119,8 +2154,10 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
* power_supply_battery_info visible in sysfs.
*/
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
return -EINVAL;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
ret = bq27xxx_battery_read_dmin_volt(di, val);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
ret = bq27xxx_battery_read_cyct(di, val);
break;

View File

@ -20,13 +20,6 @@
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | \
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
enum CROS_CHCTL_ATTR {
CROS_CHCTL_ATTR_START_THRESHOLD,
CROS_CHCTL_ATTR_END_THRESHOLD,
CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR,
_CROS_CHCTL_ATTR_COUNT
};
/*
* Semantics of data *returned* from the EC API and Linux sysfs differ
* slightly, also the v1 API can not return any data.
@ -38,18 +31,13 @@ enum CROS_CHCTL_ATTR {
*/
struct cros_chctl_priv {
struct device *dev;
struct cros_ec_device *cros_ec;
struct acpi_battery_hook battery_hook;
struct power_supply *hooked_battery;
u8 cmd_version;
/* The callbacks need to access this priv structure.
* As neither the struct device nor power_supply are under the drivers
* control, embed the attributes within priv to use with container_of().
*/
struct device_attribute device_attrs[_CROS_CHCTL_ATTR_COUNT];
struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT];
struct attribute_group group;
const struct power_supply_ext *psy_ext;
struct mutex lock; /* protects fields below and cros_ec */
enum power_supply_charge_behaviour current_behaviour;
@ -119,26 +107,39 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req);
}
static struct cros_chctl_priv *cros_chctl_attr_to_priv(struct attribute *attr,
enum CROS_CHCTL_ATTR idx)
static int cros_chctl_psy_ext_get_prop(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct device_attribute *dev_attr = container_of(attr, struct device_attribute, attr);
struct cros_chctl_priv *priv = data;
return container_of(dev_attr, struct cros_chctl_priv, device_attrs[idx]);
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
val->intval = priv->current_start_threshold;
return 0;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
val->intval = priv->current_end_threshold;
return 0;
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
val->intval = priv->current_behaviour;
return 0;
default:
return -EINVAL;
}
}
static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_priv *priv,
int is_end_threshold, const char *buf, size_t count)
static int cros_chctl_psy_ext_set_threshold(struct cros_chctl_priv *priv,
enum power_supply_property psp,
int val)
{
int ret, val;
int ret;
ret = kstrtoint(buf, 10, &val);
if (ret < 0)
return ret;
if (val < 0 || val > 100)
return -EINVAL;
if (is_end_threshold) {
if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) {
/* Start threshold is not exposed, use fixed value */
if (priv->cmd_version == 2)
priv->current_start_threshold = val == 100 ? 0 : val;
@ -158,94 +159,74 @@ static ssize_t cros_chctl_store_threshold(struct device *dev, struct cros_chctl_
return ret;
}
return count;
return 0;
}
static ssize_t charge_control_start_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static int cros_chctl_psy_ext_set_prop(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_START_THRESHOLD);
guard(mutex)(&priv->lock);
return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold);
}
static ssize_t charge_control_start_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_START_THRESHOLD);
guard(mutex)(&priv->lock);
return cros_chctl_store_threshold(dev, priv, 0, buf, count);
}
static ssize_t charge_control_end_threshold_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_END_THRESHOLD);
guard(mutex)(&priv->lock);
return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold);
}
static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_END_THRESHOLD);
guard(mutex)(&priv->lock);
return cros_chctl_store_threshold(dev, priv, 1, buf, count);
}
static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR);
guard(mutex)(&priv->lock);
return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS,
priv->current_behaviour, buf);
}
static ssize_t charge_behaviour_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR);
struct cros_chctl_priv *priv = data;
int ret;
ret = power_supply_charge_behaviour_parse(EC_CHARGE_CONTROL_BEHAVIOURS, buf);
if (ret < 0)
return ret;
guard(mutex)(&priv->lock);
priv->current_behaviour = ret;
ret = cros_chctl_configure_ec(priv);
if (ret < 0)
return ret;
return count;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
return cros_chctl_psy_ext_set_threshold(priv, psp, val->intval);
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
priv->current_behaviour = val->intval;
ret = cros_chctl_configure_ec(priv);
if (ret < 0)
return ret;
return 0;
default:
return -EINVAL;
}
}
static umode_t cros_chtl_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
static int cros_chctl_psy_prop_is_writeable(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp)
{
struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(attr, n);
if (n == CROS_CHCTL_ATTR_START_THRESHOLD && priv->cmd_version < 3)
return 0;
else if (n == CROS_CHCTL_ATTR_END_THRESHOLD && priv->cmd_version < 2)
return 0;
return attr->mode;
return true;
}
#define DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(_name, ...) \
static const enum power_supply_property _name ## _props[] = { \
__VA_ARGS__, \
}; \
\
static const struct power_supply_ext _name = { \
.name = "cros-charge-control", \
.properties = _name ## _props, \
.num_properties = ARRAY_SIZE(_name ## _props), \
.charge_behaviours = EC_CHARGE_CONTROL_BEHAVIOURS, \
.get_property = cros_chctl_psy_ext_get_prop, \
.set_property = cros_chctl_psy_ext_set_prop, \
.property_is_writeable = cros_chctl_psy_prop_is_writeable, \
}
DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v1,
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR
);
DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v2,
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD
);
DEFINE_CROS_CHCTL_POWER_SUPPLY_EXTENSION(cros_chctl_psy_ext_v3,
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD
);
static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
{
struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook);
@ -254,7 +235,7 @@ static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_batt
return 0;
priv->hooked_battery = battery;
return device_add_group(&battery->dev, &priv->group);
return power_supply_register_extension(battery, priv->psy_ext, priv->dev, priv);
}
static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
@ -262,7 +243,7 @@ static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_b
struct cros_chctl_priv *priv = container_of(hook, struct cros_chctl_priv, battery_hook);
if (priv->hooked_battery == battery) {
device_remove_group(&battery->dev, &priv->group);
power_supply_unregister_extension(battery, priv->psy_ext);
priv->hooked_battery = NULL;
}
@ -288,7 +269,6 @@ static int cros_chctl_probe(struct platform_device *pdev)
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct cros_chctl_priv *priv;
size_t i;
int ret;
ret = cros_chctl_fwk_charge_control_versions(cros_ec);
@ -321,19 +301,15 @@ static int cros_chctl_probe(struct platform_device *pdev)
dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version);
priv->dev = dev;
priv->cros_ec = cros_ec;
priv->device_attrs[CROS_CHCTL_ATTR_START_THRESHOLD] =
(struct device_attribute)__ATTR_RW(charge_control_start_threshold);
priv->device_attrs[CROS_CHCTL_ATTR_END_THRESHOLD] =
(struct device_attribute)__ATTR_RW(charge_control_end_threshold);
priv->device_attrs[CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR] =
(struct device_attribute)__ATTR_RW(charge_behaviour);
for (i = 0; i < _CROS_CHCTL_ATTR_COUNT; i++) {
sysfs_attr_init(&priv->device_attrs[i].attr);
priv->attributes[i] = &priv->device_attrs[i].attr;
}
priv->group.is_visible = cros_chtl_attr_is_visible;
priv->group.attrs = priv->attributes;
if (priv->cmd_version == 1)
priv->psy_ext = &cros_chctl_psy_ext_v1;
else if (priv->cmd_version == 2)
priv->psy_ext = &cros_chctl_psy_ext_v2;
else
priv->psy_ext = &cros_chctl_psy_ext_v3;
priv->battery_hook.name = dev_name(dev);
priv->battery_hook.add_battery = cros_chctl_add_battery;

View File

@ -195,22 +195,22 @@ static int w1_ds2760_recall_eeprom(struct device *dev, int addr)
}
static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
return w1_ds2760_read(dev, buf, off, count);
}
static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE);
static const BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE);
static struct bin_attribute *w1_ds2760_bin_attrs[] = {
static const struct bin_attribute *const w1_ds2760_bin_attrs[] = {
&bin_attr_w1_slave,
NULL,
};
static const struct attribute_group w1_ds2760_group = {
.bin_attrs = w1_ds2760_bin_attrs,
.bin_attrs_new = w1_ds2760_bin_attrs,
};
static const struct attribute_group *w1_ds2760_groups[] = {

View File

@ -621,7 +621,7 @@ static ssize_t ds2780_set_pio_pin(struct device *dev,
static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -634,7 +634,7 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -654,19 +654,19 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2780_param_eeprom_bin_attr = {
static const struct bin_attribute ds2780_param_eeprom_bin_attr = {
.attr = {
.name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2780_PARAM_EEPROM_SIZE,
.read = ds2780_read_param_eeprom_bin,
.write = ds2780_write_param_eeprom_bin,
.read_new = ds2780_read_param_eeprom_bin,
.write_new = ds2780_write_param_eeprom_bin,
};
static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -679,7 +679,7 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -699,14 +699,14 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2780_user_eeprom_bin_attr = {
static const struct bin_attribute ds2780_user_eeprom_bin_attr = {
.attr = {
.name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2780_USER_EEPROM_SIZE,
.read = ds2780_read_user_eeprom_bin,
.write = ds2780_write_user_eeprom_bin,
.read_new = ds2780_read_user_eeprom_bin,
.write_new = ds2780_write_user_eeprom_bin,
};
static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled,
@ -726,7 +726,7 @@ static struct attribute *ds2780_sysfs_attrs[] = {
NULL
};
static struct bin_attribute *ds2780_sysfs_bin_attrs[] = {
static const struct bin_attribute *const ds2780_sysfs_bin_attrs[] = {
&ds2780_param_eeprom_bin_attr,
&ds2780_user_eeprom_bin_attr,
NULL
@ -734,7 +734,7 @@ static struct bin_attribute *ds2780_sysfs_bin_attrs[] = {
static const struct attribute_group ds2780_sysfs_group = {
.attrs = ds2780_sysfs_attrs,
.bin_attrs = ds2780_sysfs_bin_attrs,
.bin_attrs_new = ds2780_sysfs_bin_attrs,
};
static const struct attribute_group *ds2780_sysfs_groups[] = {

View File

@ -623,7 +623,7 @@ static ssize_t ds2781_set_pio_pin(struct device *dev,
static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -636,7 +636,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -656,19 +656,19 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2781_param_eeprom_bin_attr = {
static const struct bin_attribute ds2781_param_eeprom_bin_attr = {
.attr = {
.name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2781_PARAM_EEPROM_SIZE,
.read = ds2781_read_param_eeprom_bin,
.write = ds2781_write_param_eeprom_bin,
.read_new = ds2781_read_param_eeprom_bin,
.write_new = ds2781_write_param_eeprom_bin,
};
static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -682,7 +682,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -702,14 +702,14 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2781_user_eeprom_bin_attr = {
static const struct bin_attribute ds2781_user_eeprom_bin_attr = {
.attr = {
.name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR,
},
.size = DS2781_USER_EEPROM_SIZE,
.read = ds2781_read_user_eeprom_bin,
.write = ds2781_write_user_eeprom_bin,
.read_new = ds2781_read_user_eeprom_bin,
.write_new = ds2781_write_user_eeprom_bin,
};
static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled,
@ -729,7 +729,7 @@ static struct attribute *ds2781_sysfs_attrs[] = {
NULL
};
static struct bin_attribute *ds2781_sysfs_bin_attrs[] = {
static const struct bin_attribute *const ds2781_sysfs_bin_attrs[] = {
&ds2781_param_eeprom_bin_attr,
&ds2781_user_eeprom_bin_attr,
NULL,
@ -737,7 +737,7 @@ static struct bin_attribute *ds2781_sysfs_bin_attrs[] = {
static const struct attribute_group ds2781_sysfs_group = {
.attrs = ds2781_sysfs_attrs,
.bin_attrs = ds2781_sysfs_bin_attrs,
.bin_attrs_new = ds2781_sysfs_bin_attrs,
};

View File

@ -11,6 +11,7 @@
* UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
*/
#include <linux/devm-helpers.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
@ -57,14 +58,12 @@ struct ds278x_info {
struct power_supply_desc battery_desc;
const struct ds278x_battery_ops *ops;
struct delayed_work bat_work;
int id;
int rsns;
int capacity;
int status; /* State Of Charge */
};
static DEFINE_IDR(battery_id);
static DEFINE_MUTEX(battery_lock);
static DEFINE_IDA(battery_id);
static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
{
@ -312,21 +311,6 @@ static void ds278x_power_supply_init(struct power_supply_desc *battery)
battery->external_power_changed = NULL;
}
static void ds278x_battery_remove(struct i2c_client *client)
{
struct ds278x_info *info = i2c_get_clientdata(client);
int id = info->id;
power_supply_unregister(info->battery);
cancel_delayed_work_sync(&info->bat_work);
kfree(info->battery_desc.name);
kfree(info);
mutex_lock(&battery_lock);
idr_remove(&battery_id, id);
mutex_unlock(&battery_lock);
}
#ifdef CONFIG_PM_SLEEP
static int ds278x_suspend(struct device *dev)
@ -368,6 +352,13 @@ static const struct ds278x_battery_ops ds278x_ops[] = {
}
};
static void ds278x_free_ida(void *data)
{
int num = (uintptr_t)data;
ida_free(&battery_id, num);
}
static int ds278x_battery_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
@ -387,32 +378,27 @@ static int ds278x_battery_probe(struct i2c_client *client)
}
/* Get an ID for this battery */
mutex_lock(&battery_lock);
ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
mutex_unlock(&battery_lock);
if (ret < 0)
goto fail_id;
num = ret;
num = ida_alloc(&battery_id, GFP_KERNEL);
if (num < 0)
return num;
ret = devm_add_action_or_reset(&client->dev, ds278x_free_ida, (void *)(uintptr_t)num);
if (ret)
return ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto fail_info;
}
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d",
client->name, num);
if (!info->battery_desc.name) {
ret = -ENOMEM;
goto fail_name;
}
info->battery_desc.name = devm_kasprintf(&client->dev, GFP_KERNEL,
"%s-%d", client->name, num);
if (!info->battery_desc.name)
return -ENOMEM;
if (id->driver_data == DS2786)
info->rsns = pdata->rsns;
i2c_set_clientdata(client, info);
info->client = client;
info->id = num;
info->ops = &ds278x_ops[id->driver_data];
ds278x_power_supply_init(&info->battery_desc);
psy_cfg.drv_data = info;
@ -420,30 +406,20 @@ static int ds278x_battery_probe(struct i2c_client *client)
info->capacity = 100;
info->status = POWER_SUPPLY_STATUS_FULL;
INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
info->battery = power_supply_register(&client->dev,
&info->battery_desc, &psy_cfg);
info->battery = devm_power_supply_register(&client->dev,
&info->battery_desc,
&psy_cfg);
if (IS_ERR(info->battery)) {
dev_err(&client->dev, "failed to register battery\n");
ret = PTR_ERR(info->battery);
goto fail_register;
} else {
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
return PTR_ERR(info->battery);
}
return 0;
ret = devm_delayed_work_autocancel(&client->dev, &info->bat_work, ds278x_bat_work);
if (ret)
return ret;
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
fail_register:
kfree(info->battery_desc.name);
fail_name:
kfree(info);
fail_info:
mutex_lock(&battery_lock);
idr_remove(&battery_id, num);
mutex_unlock(&battery_lock);
fail_id:
return ret;
return 0;
}
static const struct i2c_device_id ds278x_id[] = {
@ -459,7 +435,6 @@ static struct i2c_driver ds278x_battery_driver = {
.pm = &ds278x_battery_pm_ops,
},
.probe = ds278x_battery_probe,
.remove = ds278x_battery_remove,
.id_table = ds278x_id,
};
module_i2c_driver(ds278x_battery_driver);

View File

@ -195,6 +195,8 @@ static int init_charge_current_limit(struct device *dev,
{
int i, len;
u32 cur_limit = U32_MAX;
bool set_def_limit;
u32 def_limit;
gpio_charger->current_limit_gpios = devm_gpiod_get_array_optional(dev,
"charge-current-limit", GPIOD_OUT_LOW);
@ -228,6 +230,9 @@ static int init_charge_current_limit(struct device *dev,
if (len < 0)
return len;
set_def_limit = !device_property_read_u32(dev,
"charge-current-limit-default-microamp",
&def_limit);
for (i=0; i < gpio_charger->current_limit_map_size; i++) {
if (gpio_charger->current_limit_map[i].limit_ua > cur_limit) {
dev_err(dev, "charge-current-limit-mapping not sorted by current in descending order\n");
@ -235,8 +240,16 @@ static int init_charge_current_limit(struct device *dev,
}
cur_limit = gpio_charger->current_limit_map[i].limit_ua;
if (set_def_limit && def_limit == cur_limit) {
set_charge_current_limit(gpio_charger, cur_limit);
return 0;
}
}
if (set_def_limit)
dev_warn(dev, "charge-current-limit-default-microamp %u not listed in charge-current-limit-mapping\n",
def_limit);
/* default to smallest current limitation for safety reasons */
len = gpio_charger->current_limit_map_size - 1;
set_charge_current_limit(gpio_charger,

View File

@ -7,76 +7,154 @@
#include <linux/power_supply.h>
#include <linux/regmap.h>
#define IP5XXX_SYS_CTL0 0x01
#define IP5XXX_SYS_CTL0_WLED_DET_EN BIT(4)
#define IP5XXX_SYS_CTL0_WLED_EN BIT(3)
#define IP5XXX_SYS_CTL0_BOOST_EN BIT(2)
#define IP5XXX_SYS_CTL0_CHARGER_EN BIT(1)
#define IP5XXX_SYS_CTL1 0x02
#define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN BIT(1)
#define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN BIT(0)
#define IP5XXX_SYS_CTL2 0x0c
#define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH GENMASK(7, 3)
#define IP5XXX_SYS_CTL3 0x03
#define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL GENMASK(7, 6)
#define IP5XXX_SYS_CTL3_BTN_SHDN_EN BIT(5)
#define IP5XXX_SYS_CTL4 0x04
#define IP5XXX_SYS_CTL4_SHDN_TIME_SEL GENMASK(7, 6)
#define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN BIT(5)
#define IP5XXX_SYS_CTL5 0x07
#define IP5XXX_SYS_CTL5_NTC_DIS BIT(6)
#define IP5XXX_SYS_CTL5_WLED_MODE_SEL BIT(1)
#define IP5XXX_SYS_CTL5_BTN_SHDN_SEL BIT(0)
#define IP5XXX_CHG_CTL1 0x22
#define IP5XXX_CHG_CTL1_BOOST_UVP_SEL GENMASK(3, 2)
#define IP5XXX_CHG_CTL2 0x24
#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL GENMASK(6, 5)
#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V (0x0 << 5)
#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V (0x1 << 5)
#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V (0x2 << 5)
#define IP5XXX_CHG_CTL2_CONST_VOLT_SEL GENMASK(2, 1)
#define IP5XXX_CHG_CTL4 0x26
#define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN BIT(6)
#define IP5XXX_CHG_CTL4A 0x25
#define IP5XXX_CHG_CTL4A_CONST_CUR_SEL GENMASK(4, 0)
#define IP5XXX_MFP_CTL0 0x51
#define IP5XXX_MFP_CTL1 0x52
#define IP5XXX_GPIO_CTL2 0x53
#define IP5XXX_GPIO_CTL2A 0x54
#define IP5XXX_GPIO_CTL3 0x55
#define IP5XXX_READ0 0x71
#define IP5XXX_READ0_CHG_STAT GENMASK(7, 5)
#define IP5XXX_READ0_CHG_STAT_IDLE (0x0 << 5)
#define IP5XXX_READ0_CHG_STAT_TRICKLE (0x1 << 5)
#define IP5XXX_READ0_CHG_STAT_CONST_VOLT (0x2 << 5)
#define IP5XXX_READ0_CHG_STAT_CONST_CUR (0x3 << 5)
#define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP (0x4 << 5)
#define IP5XXX_READ0_CHG_STAT_FULL (0x5 << 5)
#define IP5XXX_READ0_CHG_STAT_TIMEOUT (0x6 << 5)
#define IP5XXX_READ0_CHG_OP BIT(4)
#define IP5XXX_READ0_CHG_END BIT(3)
#define IP5XXX_READ0_CONST_VOLT_TIMEOUT BIT(2)
#define IP5XXX_READ0_CHG_TIMEOUT BIT(1)
#define IP5XXX_READ0_TRICKLE_TIMEOUT BIT(0)
#define IP5XXX_READ0_TIMEOUT GENMASK(2, 0)
#define IP5XXX_READ1 0x72
#define IP5XXX_READ1_WLED_PRESENT BIT(7)
#define IP5XXX_READ1_LIGHT_LOAD BIT(6)
#define IP5XXX_READ1_VIN_OVERVOLT BIT(5)
#define IP5XXX_READ2 0x77
#define IP5XXX_READ2_BTN_PRESS BIT(3)
#define IP5XXX_READ2_BTN_LONG_PRESS BIT(1)
#define IP5XXX_READ2_BTN_SHORT_PRESS BIT(0)
#define IP5XXX_BATVADC_DAT0 0xa2
#define IP5XXX_BATVADC_DAT1 0xa3
#define IP5XXX_BATIADC_DAT0 0xa4
#define IP5XXX_BATIADC_DAT1 0xa5
#define IP5XXX_BATOCV_DAT0 0xa8
#define IP5XXX_BATOCV_DAT1 0xa9
#define IP5XXX_BAT_TYPE_4_2V 0x0
#define IP5XXX_BAT_TYPE_4_3V 0x1
#define IP5XXX_BAT_TYPE_4_35V 0x2
#define IP5XXX_BAT_TYPE_4_4V 0x3
#define IP5XXX_CHG_STAT_IDLE 0x0
#define IP5XXX_CHG_STAT_TRICKLE 0x1
#define IP5XXX_CHG_STAT_CONST_VOLT 0x2
#define IP5XXX_CHG_STAT_CONST_CUR 0x3
#define IP5XXX_CHG_STAT_CONST_VOLT_STOP 0x4
#define IP5XXX_CHG_STAT_FULL 0x5
#define IP5XXX_CHG_STAT_TIMEOUT 0x6
struct ip5xxx {
struct regmap *regmap;
bool initialized;
struct {
struct {
/* Charger enable */
struct regmap_field *enable;
/* Constant voltage value */
struct regmap_field *const_volt_sel;
/* Constant current value */
struct regmap_field *const_curr_sel;
/* Charger status */
struct regmap_field *status;
/* Charging ended flag */
struct regmap_field *chg_end;
/* Timeout flags (CV, charge, trickle) */
struct regmap_field *timeout;
/* Overvoltage limit */
struct regmap_field *vin_overvolt;
} charger;
struct {
/* Boost converter enable */
struct regmap_field *enable;
struct {
/* Light load shutdown enable */
struct regmap_field *enable;
/* Light load shutdown current limit */
struct regmap_field *i_limit;
} light_load_shutdown;
/* Automatic powerup on increased load */
struct regmap_field *load_powerup_en;
/* Automatic powerup on VIN pull-out */
struct regmap_field *vin_pullout_en;
/* Undervoltage limit */
struct regmap_field *undervolt_limit;
/* Light load status flag */
struct regmap_field *light_load_status;
} boost;
struct {
/* NTC disable */
struct regmap_field *ntc_dis;
/* Battery voltage type */
struct regmap_field *type;
/* Battery voltage autoset from Vset pin */
struct regmap_field *vset_en;
struct {
/* Battery measurement registers */
struct ip5xxx_battery_adc_regs {
struct regmap_field *low;
struct regmap_field *high;
} volt, curr, open_volt;
} adc;
} battery;
struct {
/* Double/long press shutdown enable */
struct regmap_field *shdn_enable;
/* WLED activation: double press or long press */
struct regmap_field *wled_mode;
/* Shutdown activation: double press or long press */
struct regmap_field *shdn_mode;
/* Long press time */
struct regmap_field *long_press_time;
/* Button pressed */
struct regmap_field *pressed;
/* Button long-pressed */
struct regmap_field *long_pressed;
/* Button short-pressed */
struct regmap_field *short_pressed;
} btn;
struct {
/* WLED enable */
struct regmap_field *enable;
/* WLED detect */
struct regmap_field *detect_en;
/* WLED present */
struct regmap_field *present;
} wled;
} regs;
/* Maximum supported battery voltage (via regs.battery.type) */
int vbat_max;
/* Scaling constants for regs.boost.undervolt_limit */
struct {
int setpoint;
int microvolts_per_bit;
} boost_undervolt;
/* Scaling constants for regs.charger.const_curr_sel */
struct {
int setpoint;
} const_curr;
/* Whether regs.charger.chg_end is inverted */
u8 chg_end_inverted;
};
#define REG_FIELD_UNSUPPORTED { .lsb = 1 }
/* Register fields layout. Unsupported registers marked as { .lsb = 1 } */
struct ip5xxx_regfield_config {
const struct reg_field charger_enable;
const struct reg_field charger_const_volt_sel;
const struct reg_field charger_const_curr_sel;
const struct reg_field charger_status;
const struct reg_field charger_chg_end;
const struct reg_field charger_timeout;
const struct reg_field charger_vin_overvolt;
const struct reg_field boost_enable;
const struct reg_field boost_llshdn_enable;
const struct reg_field boost_llshdn_i_limit;
const struct reg_field boost_load_powerup_en;
const struct reg_field boost_vin_pullout_en;
const struct reg_field boost_undervolt_limit;
const struct reg_field boost_light_load_status;
const struct reg_field battery_ntc_dis;
const struct reg_field battery_type;
const struct reg_field battery_vset_en;
const struct reg_field battery_adc_volt_low;
const struct reg_field battery_adc_volt_high;
const struct reg_field battery_adc_curr_low;
const struct reg_field battery_adc_curr_high;
const struct reg_field battery_adc_ovolt_low;
const struct reg_field battery_adc_ovolt_high;
const struct reg_field btn_shdn_enable;
const struct reg_field btn_wled_mode;
const struct reg_field btn_shdn_mode;
const struct reg_field btn_long_press_time;
const struct reg_field btn_pressed;
const struct reg_field btn_long_pressed;
const struct reg_field btn_short_pressed;
const struct reg_field wled_enable;
const struct reg_field wled_detect_en;
const struct reg_field wled_present;
int vbat_max;
int boost_undervolt_setpoint;
int boost_undervolt_uv_per_bit;
int const_curr_setpoint;
u8 chg_end_inverted;
};
/*
@ -87,24 +165,30 @@ struct ip5xxx {
* 2) Attempt the initialization sequence on each subsequent register access
* until it succeeds.
*/
static int ip5xxx_read(struct ip5xxx *ip5xxx, unsigned int reg,
static int ip5xxx_read(struct ip5xxx *ip5xxx, struct regmap_field *field,
unsigned int *val)
{
int ret;
ret = regmap_read(ip5xxx->regmap, reg, val);
if (!field)
return -EOPNOTSUPP;
ret = regmap_field_read(field, val);
if (ret)
ip5xxx->initialized = false;
return ret;
}
static int ip5xxx_update_bits(struct ip5xxx *ip5xxx, unsigned int reg,
unsigned int mask, unsigned int val)
static int ip5xxx_write(struct ip5xxx *ip5xxx, struct regmap_field *field,
unsigned int val)
{
int ret;
ret = regmap_update_bits(ip5xxx->regmap, reg, mask, val);
if (!field)
return -EOPNOTSUPP;
ret = regmap_field_write(field, val);
if (ret)
ip5xxx->initialized = false;
@ -123,28 +207,26 @@ static int ip5xxx_initialize(struct power_supply *psy)
* Disable shutdown under light load.
* Enable power on when under load.
*/
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL1,
IP5XXX_SYS_CTL1_LIGHT_SHDN_EN |
IP5XXX_SYS_CTL1_LOAD_PWRUP_EN,
IP5XXX_SYS_CTL1_LOAD_PWRUP_EN);
if (ip5xxx->regs.boost.light_load_shutdown.enable) {
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.light_load_shutdown.enable, 0);
if (ret)
return ret;
}
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.load_powerup_en, 1);
if (ret)
return ret;
/*
* Enable shutdown after a long button press (as configured below).
*/
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL3,
IP5XXX_SYS_CTL3_BTN_SHDN_EN,
IP5XXX_SYS_CTL3_BTN_SHDN_EN);
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_enable, 1);
if (ret)
return ret;
/*
* Power on automatically when VIN is removed.
*/
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL4,
IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN,
IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN);
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.vin_pullout_en, 1);
if (ret)
return ret;
@ -152,12 +234,15 @@ static int ip5xxx_initialize(struct power_supply *psy)
* Enable the NTC.
* Configure the button for two presses => LED, long press => shutdown.
*/
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL5,
IP5XXX_SYS_CTL5_NTC_DIS |
IP5XXX_SYS_CTL5_WLED_MODE_SEL |
IP5XXX_SYS_CTL5_BTN_SHDN_SEL,
IP5XXX_SYS_CTL5_WLED_MODE_SEL |
IP5XXX_SYS_CTL5_BTN_SHDN_SEL);
if (ip5xxx->regs.battery.ntc_dis) {
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.ntc_dis, 0);
if (ret)
return ret;
}
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.wled_mode, 1);
if (ret)
return ret;
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_mode, 1);
if (ret)
return ret;
@ -186,24 +271,37 @@ static int ip5xxx_battery_get_status(struct ip5xxx *ip5xxx, int *val)
unsigned int rval;
int ret;
ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
if (!ip5xxx->regs.charger.status) {
// Fall-back to Charging Ended bit
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.chg_end, &rval);
if (ret)
return ret;
if (rval == ip5xxx->chg_end_inverted)
*val = POWER_SUPPLY_STATUS_CHARGING;
else
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
}
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval);
if (ret)
return ret;
switch (rval & IP5XXX_READ0_CHG_STAT) {
case IP5XXX_READ0_CHG_STAT_IDLE:
switch (rval) {
case IP5XXX_CHG_STAT_IDLE:
*val = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case IP5XXX_READ0_CHG_STAT_TRICKLE:
case IP5XXX_READ0_CHG_STAT_CONST_CUR:
case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
case IP5XXX_CHG_STAT_TRICKLE:
case IP5XXX_CHG_STAT_CONST_CUR:
case IP5XXX_CHG_STAT_CONST_VOLT:
*val = POWER_SUPPLY_STATUS_CHARGING;
break;
case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
case IP5XXX_READ0_CHG_STAT_FULL:
case IP5XXX_CHG_STAT_CONST_VOLT_STOP:
case IP5XXX_CHG_STAT_FULL:
*val = POWER_SUPPLY_STATUS_FULL;
break;
case IP5XXX_READ0_CHG_STAT_TIMEOUT:
case IP5XXX_CHG_STAT_TIMEOUT:
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
default:
@ -218,22 +316,22 @@ static int ip5xxx_battery_get_charge_type(struct ip5xxx *ip5xxx, int *val)
unsigned int rval;
int ret;
ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval);
if (ret)
return ret;
switch (rval & IP5XXX_READ0_CHG_STAT) {
case IP5XXX_READ0_CHG_STAT_IDLE:
case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
case IP5XXX_READ0_CHG_STAT_FULL:
case IP5XXX_READ0_CHG_STAT_TIMEOUT:
switch (rval) {
case IP5XXX_CHG_STAT_IDLE:
case IP5XXX_CHG_STAT_CONST_VOLT_STOP:
case IP5XXX_CHG_STAT_FULL:
case IP5XXX_CHG_STAT_TIMEOUT:
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
case IP5XXX_READ0_CHG_STAT_TRICKLE:
case IP5XXX_CHG_STAT_TRICKLE:
*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case IP5XXX_READ0_CHG_STAT_CONST_CUR:
case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
case IP5XXX_CHG_STAT_CONST_CUR:
case IP5XXX_CHG_STAT_CONST_VOLT:
*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
break;
default:
@ -248,11 +346,11 @@ static int ip5xxx_battery_get_health(struct ip5xxx *ip5xxx, int *val)
unsigned int rval;
int ret;
ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.timeout, &rval);
if (ret)
return ret;
if (rval & IP5XXX_READ0_TIMEOUT)
if (rval)
*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
else
*val = POWER_SUPPLY_HEALTH_GOOD;
@ -265,7 +363,7 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
unsigned int rval;
int ret;
ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.battery.type, &rval);
if (ret)
return ret;
@ -273,16 +371,19 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
* It is not clear what this will return if
* IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set...
*/
switch (rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL) {
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V:
switch (rval) {
case IP5XXX_BAT_TYPE_4_2V:
*val = 4200000;
break;
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V:
case IP5XXX_BAT_TYPE_4_3V:
*val = 4300000;
break;
case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V:
case IP5XXX_BAT_TYPE_4_35V:
*val = 4350000;
break;
case IP5XXX_BAT_TYPE_4_4V:
*val = 4400000;
break;
default:
return -EINVAL;
}
@ -291,16 +392,16 @@ static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
}
static int ip5xxx_battery_read_adc(struct ip5xxx *ip5xxx,
u8 lo_reg, u8 hi_reg, int *val)
struct ip5xxx_battery_adc_regs *regs, int *val)
{
unsigned int hi, lo;
int ret;
ret = ip5xxx_read(ip5xxx, lo_reg, &lo);
ret = ip5xxx_read(ip5xxx, regs->low, &lo);
if (ret)
return ret;
ret = ip5xxx_read(ip5xxx, hi_reg, &hi);
ret = ip5xxx_read(ip5xxx, regs->high, &hi);
if (ret)
return ret;
@ -335,33 +436,35 @@ static int ip5xxx_battery_get_property(struct power_supply *psy,
return ip5xxx_battery_get_voltage_max(ip5xxx, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATVADC_DAT0,
IP5XXX_BATVADC_DAT1, &raw);
ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.volt, &raw);
if (ret)
return ret;
val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
return 0;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATOCV_DAT0,
IP5XXX_BATOCV_DAT1, &raw);
ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.open_volt, &raw);
if (ret)
return ret;
val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
return 0;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATIADC_DAT0,
IP5XXX_BATIADC_DAT1, &raw);
ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.curr, &raw);
if (ret)
return ret;
val->intval = DIV_ROUND_CLOSEST(raw * 149197, 200);
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL4A, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_curr_sel, &rval);
if (ret)
return ret;
rval &= IP5XXX_CHG_CTL4A_CONST_CUR_SEL;
val->intval = 100000 * rval;
val->intval = ip5xxx->const_curr.setpoint + 100000 * rval;
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
@ -373,12 +476,11 @@ static int ip5xxx_battery_get_property(struct power_supply *psy,
if (ret)
return ret;
ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_volt_sel, &rval);
if (ret)
return ret;
rval &= IP5XXX_CHG_CTL2_CONST_VOLT_SEL;
val->intval = vmax + 14000 * (rval >> 1);
val->intval = vmax + 14000 * rval;
return 0;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
@ -399,30 +501,36 @@ static int ip5xxx_battery_set_voltage_max(struct ip5xxx *ip5xxx, int val)
unsigned int rval;
int ret;
if (val > ip5xxx->vbat_max)
return -EINVAL;
switch (val) {
case 4200000:
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V;
rval = IP5XXX_BAT_TYPE_4_2V;
break;
case 4300000:
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V;
rval = IP5XXX_BAT_TYPE_4_3V;
break;
case 4350000:
rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V;
rval = IP5XXX_BAT_TYPE_4_35V;
break;
case 4400000:
rval = IP5XXX_BAT_TYPE_4_4V;
break;
default:
return -EINVAL;
}
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
IP5XXX_CHG_CTL2_BAT_TYPE_SEL, rval);
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.type, rval);
if (ret)
return ret;
ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4,
IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN,
IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN);
if (ret)
return ret;
/* Don't try to auto-detect battery type, even if the IC could */
if (ip5xxx->regs.battery.vset_en) {
ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.vset_en, 1);
if (ret)
return ret;
}
return 0;
}
@ -443,7 +551,7 @@ static int ip5xxx_battery_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_STATUS:
switch (val->intval) {
case POWER_SUPPLY_STATUS_CHARGING:
rval = IP5XXX_SYS_CTL0_CHARGER_EN;
rval = 1;
break;
case POWER_SUPPLY_STATUS_DISCHARGING:
case POWER_SUPPLY_STATUS_NOT_CHARGING:
@ -452,25 +560,22 @@ static int ip5xxx_battery_set_property(struct power_supply *psy,
default:
return -EINVAL;
}
return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
IP5XXX_SYS_CTL0_CHARGER_EN, rval);
return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.enable, rval);
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
return ip5xxx_battery_set_voltage_max(ip5xxx, val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
rval = val->intval / 100000;
return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4A,
IP5XXX_CHG_CTL4A_CONST_CUR_SEL, rval);
rval = (val->intval - ip5xxx->const_curr.setpoint) / 100000;
return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_curr_sel, rval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
if (ret)
return ret;
rval = ((val->intval - vmax) / 14000) << 1;
return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
IP5XXX_CHG_CTL2_CONST_VOLT_SEL, rval);
rval = (val->intval - vmax) / 14000;
return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_volt_sel, rval);
default:
return -EINVAL;
@ -515,20 +620,20 @@ static int ip5xxx_boost_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = ip5xxx_read(ip5xxx, IP5XXX_SYS_CTL0, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.enable, &rval);
if (ret)
return ret;
val->intval = !!(rval & IP5XXX_SYS_CTL0_BOOST_EN);
val->intval = !!rval;
return 0;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL1, &rval);
ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.undervolt_limit, &rval);
if (ret)
return ret;
rval &= IP5XXX_CHG_CTL1_BOOST_UVP_SEL;
val->intval = 4530000 + 100000 * (rval >> 2);
val->intval = ip5xxx->boost_undervolt.setpoint +
ip5xxx->boost_undervolt.microvolts_per_bit * rval;
return 0;
default:
@ -550,14 +655,12 @@ static int ip5xxx_boost_set_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
rval = val->intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0;
return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
IP5XXX_SYS_CTL0_BOOST_EN, rval);
return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.enable, !!val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
rval = ((val->intval - 4530000) / 100000) << 2;
return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL1,
IP5XXX_CHG_CTL1_BOOST_UVP_SEL, rval);
rval = (val->intval - ip5xxx->boost_undervolt.setpoint) /
ip5xxx->boost_undervolt.microvolts_per_bit;
return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.undervolt_limit, rval);
default:
return -EINVAL;
@ -583,13 +686,152 @@ static const struct power_supply_desc ip5xxx_boost_desc = {
static const struct regmap_config ip5xxx_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = IP5XXX_BATOCV_DAT1,
.max_register = 0xa9,
};
static struct ip5xxx_regfield_config ip51xx_fields = {
.charger_enable = REG_FIELD(0x01, 1, 1),
.charger_const_volt_sel = REG_FIELD(0x24, 1, 2),
.charger_const_curr_sel = REG_FIELD(0x25, 0, 4),
.charger_status = REG_FIELD(0x71, 5, 7),
.charger_chg_end = REG_FIELD(0x71, 3, 3),
.charger_timeout = REG_FIELD(0x71, 0, 2),
.charger_vin_overvolt = REG_FIELD(0x72, 5, 5),
.boost_enable = REG_FIELD(0x01, 2, 2),
.boost_llshdn_enable = REG_FIELD(0x02, 1, 1),
.boost_llshdn_i_limit = REG_FIELD(0x0c, 3, 7),
.boost_load_powerup_en = REG_FIELD(0x02, 0, 0),
.boost_vin_pullout_en = REG_FIELD(0x04, 5, 5),
.boost_undervolt_limit = REG_FIELD(0x22, 2, 3),
.boost_light_load_status = REG_FIELD(0x72, 6, 6),
.battery_ntc_dis = REG_FIELD(0x07, 6, 6),
.battery_type = REG_FIELD(0x24, 5, 6),
.battery_vset_en = REG_FIELD(0x26, 6, 6),
.battery_adc_volt_low = REG_FIELD(0xa2, 0, 7),
.battery_adc_volt_high = REG_FIELD(0xa3, 0, 5),
.battery_adc_curr_low = REG_FIELD(0xa4, 0, 7),
.battery_adc_curr_high = REG_FIELD(0xa5, 0, 5),
.battery_adc_ovolt_low = REG_FIELD(0xa8, 0, 7),
.battery_adc_ovolt_high = REG_FIELD(0xa9, 0, 5),
.btn_shdn_enable = REG_FIELD(0x03, 5, 5),
.btn_wled_mode = REG_FIELD(0x07, 1, 1),
.btn_shdn_mode = REG_FIELD(0x07, 0, 0),
.btn_long_press_time = REG_FIELD(0x03, 6, 7),
.btn_pressed = REG_FIELD(0x77, 3, 3),
.btn_long_pressed = REG_FIELD(0x77, 1, 1),
.btn_short_pressed = REG_FIELD(0x77, 0, 0),
.wled_enable = REG_FIELD(0x01, 3, 3),
.wled_detect_en = REG_FIELD(0x01, 4, 4),
.wled_present = REG_FIELD(0x72, 7, 7),
.vbat_max = 4350000,
.boost_undervolt_setpoint = 4530000,
.boost_undervolt_uv_per_bit = 100000,
};
static struct ip5xxx_regfield_config ip5306_fields = {
.charger_enable = REG_FIELD(0x00, 4, 4),
.charger_const_volt_sel = REG_FIELD(0x22, 0, 1),
.charger_const_curr_sel = REG_FIELD(0x24, 0, 4),
.charger_status = REG_FIELD_UNSUPPORTED, // other bits...
.charger_chg_end = REG_FIELD(0x71, 3, 3),
.charger_timeout = REG_FIELD_UNSUPPORTED,
.charger_vin_overvolt = REG_FIELD_UNSUPPORTED,
.boost_enable = REG_FIELD(0x00, 5, 5),
.boost_llshdn_enable = REG_FIELD_UNSUPPORTED,
.boost_llshdn_i_limit = REG_FIELD_UNSUPPORTED,
.boost_load_powerup_en = REG_FIELD(0x00, 2, 2),
.boost_vin_pullout_en = REG_FIELD(0x01, 2, 2),
.boost_undervolt_limit = REG_FIELD(0x21, 2, 4),
.boost_light_load_status = REG_FIELD(0x72, 2, 2),
.battery_ntc_dis = REG_FIELD_UNSUPPORTED,
.battery_type = REG_FIELD(0x22, 2, 3),
.battery_vset_en = REG_FIELD_UNSUPPORTED,
.battery_adc_volt_low = REG_FIELD_UNSUPPORTED,
.battery_adc_volt_high = REG_FIELD_UNSUPPORTED,
.battery_adc_curr_low = REG_FIELD_UNSUPPORTED,
.battery_adc_curr_high = REG_FIELD_UNSUPPORTED,
.battery_adc_ovolt_low = REG_FIELD_UNSUPPORTED,
.battery_adc_ovolt_high = REG_FIELD_UNSUPPORTED,
.btn_shdn_enable = REG_FIELD(0x00, 0, 0),
.btn_wled_mode = REG_FIELD(0x01, 6, 6),
.btn_shdn_mode = REG_FIELD(0x01, 7, 7),
.btn_long_press_time = REG_FIELD(0x02, 4, 4), // +1s
.btn_pressed = REG_FIELD_UNSUPPORTED,
/* TODO: double press */
.btn_long_pressed = REG_FIELD(0x77, 1, 1),
.btn_short_pressed = REG_FIELD(0x77, 0, 0),
.wled_enable = REG_FIELD_UNSUPPORTED,
.wled_detect_en = REG_FIELD_UNSUPPORTED,
.wled_present = REG_FIELD_UNSUPPORTED,
.vbat_max = 4400000,
.boost_undervolt_setpoint = 4450000,
.boost_undervolt_uv_per_bit = 50000,
.const_curr_setpoint = 50000,
.chg_end_inverted = 1,
};
#define ip5xxx_setup_reg(_field, _reg) \
do { \
if (likely(cfg->_field.lsb <= cfg->_field.msb)) { \
struct regmap_field *_tmp = devm_regmap_field_alloc(dev, \
ip5xxx->regmap, cfg->_field); \
if (!IS_ERR(_tmp)) \
ip5xxx->regs._reg = _tmp; \
} \
} while (0)
static void ip5xxx_setup_regs(struct device *dev, struct ip5xxx *ip5xxx,
const struct ip5xxx_regfield_config *cfg)
{
ip5xxx_setup_reg(charger_enable, charger.enable);
ip5xxx_setup_reg(charger_const_volt_sel, charger.const_volt_sel);
ip5xxx_setup_reg(charger_const_curr_sel, charger.const_curr_sel);
ip5xxx_setup_reg(charger_status, charger.status);
ip5xxx_setup_reg(charger_chg_end, charger.chg_end);
ip5xxx_setup_reg(charger_timeout, charger.timeout);
ip5xxx_setup_reg(charger_vin_overvolt, charger.vin_overvolt);
ip5xxx_setup_reg(boost_enable, boost.enable);
ip5xxx_setup_reg(boost_llshdn_enable, boost.light_load_shutdown.enable);
ip5xxx_setup_reg(boost_llshdn_i_limit, boost.light_load_shutdown.i_limit);
ip5xxx_setup_reg(boost_load_powerup_en, boost.load_powerup_en);
ip5xxx_setup_reg(boost_vin_pullout_en, boost.vin_pullout_en);
ip5xxx_setup_reg(boost_undervolt_limit, boost.undervolt_limit);
ip5xxx_setup_reg(boost_light_load_status, boost.light_load_status);
ip5xxx_setup_reg(battery_ntc_dis, battery.ntc_dis);
ip5xxx_setup_reg(battery_type, battery.type);
ip5xxx_setup_reg(battery_vset_en, battery.vset_en);
ip5xxx_setup_reg(battery_adc_volt_low, battery.adc.volt.low);
ip5xxx_setup_reg(battery_adc_volt_high, battery.adc.volt.high);
ip5xxx_setup_reg(battery_adc_curr_low, battery.adc.curr.low);
ip5xxx_setup_reg(battery_adc_curr_high, battery.adc.curr.high);
ip5xxx_setup_reg(battery_adc_ovolt_low, battery.adc.open_volt.low);
ip5xxx_setup_reg(battery_adc_ovolt_high, battery.adc.open_volt.high);
ip5xxx_setup_reg(btn_shdn_enable, btn.shdn_enable);
ip5xxx_setup_reg(btn_wled_mode, btn.wled_mode);
ip5xxx_setup_reg(btn_shdn_mode, btn.shdn_mode);
ip5xxx_setup_reg(btn_long_press_time, btn.long_press_time);
ip5xxx_setup_reg(btn_pressed, btn.pressed);
ip5xxx_setup_reg(btn_long_pressed, btn.long_pressed);
ip5xxx_setup_reg(btn_short_pressed, btn.short_pressed);
ip5xxx_setup_reg(wled_enable, wled.enable);
ip5xxx_setup_reg(wled_detect_en, wled.detect_en);
ip5xxx_setup_reg(wled_present, wled.present);
ip5xxx->vbat_max = cfg->vbat_max;
ip5xxx->boost_undervolt.setpoint = cfg->boost_undervolt_setpoint;
ip5xxx->boost_undervolt.microvolts_per_bit = cfg->boost_undervolt_uv_per_bit;
ip5xxx->const_curr.setpoint = cfg->const_curr_setpoint;
ip5xxx->chg_end_inverted = cfg->chg_end_inverted;
}
static int ip5xxx_power_probe(struct i2c_client *client)
{
const struct ip5xxx_regfield_config *fields = &ip51xx_fields;
struct power_supply_config psy_cfg = {};
struct device *dev = &client->dev;
const struct of_device_id *of_id;
struct power_supply *psy;
struct ip5xxx *ip5xxx;
@ -601,6 +843,11 @@ static int ip5xxx_power_probe(struct i2c_client *client)
if (IS_ERR(ip5xxx->regmap))
return PTR_ERR(ip5xxx->regmap);
of_id = i2c_of_match_device(dev->driver->of_match_table, client);
if (of_id)
fields = (const struct ip5xxx_regfield_config *)of_id->data;
ip5xxx_setup_regs(dev, ip5xxx, fields);
psy_cfg.of_node = dev->of_node;
psy_cfg.drv_data = ip5xxx;
@ -616,10 +863,11 @@ static int ip5xxx_power_probe(struct i2c_client *client)
}
static const struct of_device_id ip5xxx_power_of_match[] = {
{ .compatible = "injoinic,ip5108" },
{ .compatible = "injoinic,ip5109" },
{ .compatible = "injoinic,ip5207" },
{ .compatible = "injoinic,ip5209" },
{ .compatible = "injoinic,ip5108", .data = &ip51xx_fields },
{ .compatible = "injoinic,ip5109", .data = &ip51xx_fields },
{ .compatible = "injoinic,ip5207", .data = &ip51xx_fields },
{ .compatible = "injoinic,ip5209", .data = &ip51xx_fields },
{ .compatible = "injoinic,ip5306", .data = &ip5306_fields },
{ }
};
MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match);

View File

@ -1,9 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Analog Devices (Linear Technology) LTC4162-L charger IC.
* Driver for Analog Devices (Linear Technology)
* LTC4162-L 35V/3.2A Multi-Cell Lithium-Ion Step-Down Battery Charger
* LTC4162-F 35V/3.2A Multi-Cell LiFePO4 Step-Down Battery Charger
* LTC4162-S 35V/3.2A Lead-Acid Step-Down Battery Charger
* LTC4015 35V/3.2A Multichemistry Buck Battery Charger Controller
* Copyright (C) 2020, Topic Embedded Products
*/
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/of.h>
@ -47,6 +52,20 @@
#define LTC4162L_VBAT_FILT 0x47
#define LTC4162L_INPUT_UNDERVOLTAGE_DAC 0x4B
#define LTC4162L_CHEM_MASK GENMASK(11, 8)
enum ltc4162_chem {
ltc4162_lad,
ltc4162_l42,
ltc4162_l41,
ltc4162_l40,
ltc4162_fad,
ltc4162_ffs,
ltc4162_fst,
ltc4162_sst = 8,
ltc4162_sad,
};
/* Enumeration as in datasheet. Individual bits are mutually exclusive. */
enum ltc4162l_state {
battery_detection = 2048,
@ -75,10 +94,28 @@ enum ltc4162l_charge_status {
/* Magic number to write to ARM_SHIP_MODE register */
#define LTC4162L_ARM_SHIP_MODE_MAGIC 21325
struct ltc4162l_info;
struct ltc4162l_chip_info {
const char *name;
int (*get_vbat)(struct ltc4162l_info *info, unsigned int reg,
union power_supply_propval *val);
int (*get_vcharge)(struct ltc4162l_info *info, unsigned int reg,
union power_supply_propval *val);
int (*set_vcharge)(struct ltc4162l_info *info, unsigned int reg,
unsigned int value);
int (*get_die_temp)(struct ltc4162l_info *info,
union power_supply_propval *val);
unsigned int ibat_resolution_pv;
unsigned int vin_resolution_uv;
u8 telemetry_mask;
};
struct ltc4162l_info {
struct i2c_client *client;
struct regmap *regmap;
struct power_supply *charger;
const struct ltc4162l_chip_info *chip_info;
u32 rsnsb; /* Series resistor that sets charge current, microOhm */
u32 rsnsi; /* Series resistor to measure input current, microOhm */
u8 cell_count; /* Number of connected cells, 0 while unknown */
@ -108,6 +145,18 @@ static u8 ltc4162l_get_cell_count(struct ltc4162l_info *info)
return val;
};
static u8 ltc4162l_get_chem_type(struct ltc4162l_info *info)
{
int ret;
unsigned int val;
ret = regmap_read(info->regmap, LTC4162L_CHEM_CELLS_REG, &val);
if (ret)
return ret;
return FIELD_GET(LTC4162L_CHEM_MASK, val);
};
/* Convert enum value to POWER_SUPPLY_STATUS value */
static int ltc4162l_state_decode(enum ltc4162l_state value)
{
@ -223,25 +272,83 @@ static int ltc4162l_get_vbat(struct ltc4162l_info *info,
unsigned int reg,
union power_supply_propval *val)
{
unsigned int regval;
unsigned int regval, chem_type;
int ret;
ret = regmap_read(info->regmap, reg, &regval);
if (ret)
return ret;
/* cell_count × 192.4μV/LSB */
regval *= 1924;
regval *= ltc4162l_get_cell_count(info);
regval /= 10;
val->intval = regval;
/*
* cell_count × scaling factor
* For ltc4162-s, it uses a cell_count value of 2 for each group of 3
* physical (2V) cells, thus will return 2, 4, 6, 8 for 6V, 12V, 18V,
* and 24V respectively, and has to divide by 2 to multiply the scale
* factor by 1, 2, 3, or 4 to represent a 6V, 12V, 18V, or 24V battery
* respectively.
*/
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_fst:
regval *= 1924;
regval *= ltc4162l_get_cell_count(info);
regval /= 10;
val->intval = regval;
return 0;
return 0;
case ltc4162_sst ... ltc4162_sad:
regval *= 3848;
regval *= ltc4162l_get_cell_count(info) / 2;
regval /= 10;
val->intval = regval;
return 0;
default:
return -EINVAL;
}
}
static int ltc4015_get_vbat(struct ltc4162l_info *info,
unsigned int reg,
union power_supply_propval *val)
{
unsigned int regval, chem_type;
int ret;
ret = regmap_read(info->regmap, reg, &regval);
if (ret)
return ret;
/*
* cell count x scaling factor
* ltc4015 lead-acid fixed and lead-acid programmable corresponds to
* 0x7 and 0x8 chem respectively
*/
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_fst:
regval *= 192264;
regval *= ltc4162l_get_cell_count(info);
regval /= 1000;
val->intval = regval;
return 0;
case ltc4162_sst - 1 ... ltc4162_sad - 1:
regval *= 128176;
regval *= ltc4162l_get_cell_count(info);
regval /= 1000;
val->intval = regval;
return 0;
default:
return -EINVAL;
}
}
static int ltc4162l_get_ibat(struct ltc4162l_info *info,
union power_supply_propval *val)
{
const struct ltc4162l_chip_info *chip_info = info->chip_info;
unsigned int regval;
int ret;
@ -249,9 +356,8 @@ static int ltc4162l_get_ibat(struct ltc4162l_info *info,
if (ret)
return ret;
/* Signed 16-bit number, 1.466μV / RSNSB amperes/LSB. */
ret = (s16)(regval & 0xFFFF);
val->intval = 100 * mult_frac(ret, 14660, (int)info->rsnsb);
val->intval = mult_frac(ret, chip_info->ibat_resolution_pv, info->rsnsb);
return 0;
}
@ -260,6 +366,7 @@ static int ltc4162l_get_ibat(struct ltc4162l_info *info,
static int ltc4162l_get_input_voltage(struct ltc4162l_info *info,
union power_supply_propval *val)
{
const struct ltc4162l_chip_info *chip_info = info->chip_info;
unsigned int regval;
int ret;
@ -267,8 +374,7 @@ static int ltc4162l_get_input_voltage(struct ltc4162l_info *info,
if (ret)
return ret;
/* 1.649mV/LSB */
val->intval = regval * 1694;
val->intval = regval * chip_info->vin_resolution_uv;
return 0;
}
@ -276,6 +382,7 @@ static int ltc4162l_get_input_voltage(struct ltc4162l_info *info,
static int ltc4162l_get_input_current(struct ltc4162l_info *info,
union power_supply_propval *val)
{
const struct ltc4162l_chip_info *chip_info = info->chip_info;
unsigned int regval;
int ret;
@ -283,11 +390,9 @@ static int ltc4162l_get_input_current(struct ltc4162l_info *info,
if (ret)
return ret;
/* Signed 16-bit number, 1.466μV / RSNSI amperes/LSB. */
ret = (s16)(regval & 0xFFFF);
ret *= 14660;
ret *= chip_info->ibat_resolution_pv;
ret /= info->rsnsi;
ret *= 100;
val->intval = ret;
@ -336,7 +441,7 @@ static int ltc4162l_get_vcharge(struct ltc4162l_info *info,
unsigned int reg,
union power_supply_propval *val)
{
unsigned int regval;
unsigned int regval, chem_type;
int ret;
u32 voltage;
@ -348,37 +453,177 @@ static int ltc4162l_get_vcharge(struct ltc4162l_info *info,
/*
* charge voltage setting can be computed from
* cell_count × (vcharge_setting × 12.5mV + 3.8125V)
* where vcharge_setting ranges from 0 to 31 (4.2V max).
* cell_count × (vcharge_setting × a + b)
* where vcharge_setting ranges from 0 to c (d).
* for ltc4162l: a = 12.5mV , b = 3.8125V, c = 31, d = 4.2Vmax
* for ltc4162f: a = 12.5mV , b = 3.4125V, c = 31, d = 3.8Vmax
*
* for ltc4162s, the charge voltage setting can be computed from
* N x (vcharge_setting x 28.571mV + 6.0V)
* where N is 1, 2, 3, or 4 for 6V, 12V, 18V, or 24V battery respectively,
* and vcharge_setting ranges from 0 to 31
*/
voltage = 3812500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_l40:
voltage = 3812500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0;
return 0;
case ltc4162_fad ... ltc4162_fst:
voltage = 3412500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0;
case ltc4162_sst ... ltc4162_sad:
voltage = 6000000 + (regval * 28571);
voltage *= ltc4162l_get_cell_count(info) / 2;
val->intval = voltage;
return 0;
default:
return -EINVAL;
}
}
static int ltc4015_get_vcharge(struct ltc4162l_info *info,
unsigned int reg,
union power_supply_propval *val)
{
unsigned int regval, chem_type;
int ret;
u32 voltage;
ret = regmap_read(info->regmap, reg, &regval);
if (ret)
return ret;
regval &= BIT(6) - 1; /* Only the lower 5 bits */
/*
* charge voltage setting can be computed from:
* cell_count × (vcharge_setting × a + b)
* where vcharge_setting ranges from 0 to c (d).
* Li-Ion: a = 1/80V, b = 3.8125V, c = 31, d = 4.2Vmax
* LiFePO4: a = 1/80V, b = 3.4125V, c = 31, d = 3.8Vmax
* Lead Acid: a = 1/105V, b = 2V, c = 35, d = 2.6Vmax
*/
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_l40:
voltage = 3812500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0;
case ltc4162_fad ... ltc4162_fst:
voltage = 3412500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0;
case ltc4162_sst - 1 ... ltc4162_sad - 1:
voltage = 2000000 + mult_frac(regval, 1000000, 105);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0;
default:
return -EINVAL;
}
}
static int ltc4162l_vcharge(unsigned int base_voltage,
unsigned int scale_factor,
unsigned int range,
unsigned int value,
u8 cell_count)
{
value /= cell_count;
if (value < base_voltage)
return -EINVAL;
value -= base_voltage;
value /= scale_factor;
if (value > range)
return -EINVAL;
return value;
}
static int ltc4162l_set_vcharge(struct ltc4162l_info *info,
unsigned int reg,
unsigned int value)
{
u8 cell_count = ltc4162l_get_cell_count(info);
unsigned int chem_type;
u8 cell_count;
if (!cell_count)
return -EBUSY; /* Not available yet, try again later */
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_l40:
cell_count = ltc4162l_get_cell_count(info);
if (!cell_count)
return -EBUSY;
value /= cell_count;
value = ltc4162l_vcharge(3812500, 12500, 31, value, cell_count);
return regmap_write(info->regmap, reg, value);
case ltc4162_fad ... ltc4162_fst:
cell_count = ltc4162l_get_cell_count(info);
if (!cell_count)
return -EBUSY;
if (value < 3812500)
value = ltc4162l_vcharge(3412500, 12500, 31, value, cell_count);
return regmap_write(info->regmap, reg, value);
case ltc4162_sst ... ltc4162_sad:
cell_count = ltc4162l_get_cell_count(info) / 2;
if (!cell_count)
return -EBUSY;
value = ltc4162l_vcharge(6000000, 28571, 31, value, cell_count);
return regmap_write(info->regmap, reg, value);
default:
return -EINVAL;
}
}
value -= 3812500;
value /= 12500;
static int ltc4015_set_vcharge(struct ltc4162l_info *info,
unsigned int reg,
unsigned int value)
{
unsigned int chem_type;
u8 cell_count;
if (value > 31)
chem_type = ltc4162l_get_chem_type(info);
switch (chem_type) {
case ltc4162_lad ... ltc4162_l40:
cell_count = ltc4162l_get_cell_count(info);
if (!cell_count)
return -EBUSY;
value = ltc4162l_vcharge(3812500, 12500, 31, value, cell_count);
return regmap_write(info->regmap, reg, value);
case ltc4162_fad ... ltc4162_fst:
cell_count = ltc4162l_get_cell_count(info);
if (!cell_count)
return -EBUSY;
value = ltc4162l_vcharge(3412500, 12500, 31, value, cell_count);
return regmap_write(info->regmap, reg, value);
case ltc4162_sst - 1 ... ltc4162_sad - 1:
cell_count = ltc4162l_get_cell_count(info);
if (!cell_count)
return -EBUSY;
value = ltc4162l_vcharge(2000000, 1000000 / 105, 35,
value, cell_count);
return regmap_write(info->regmap, reg, value);
default:
return -EINVAL;
return regmap_write(info->regmap, reg, value);
}
}
static int ltc4162l_get_iin_limit_dac(struct ltc4162l_info *info,
@ -437,9 +682,30 @@ static int ltc4162l_get_die_temp(struct ltc4162l_info *info,
return 0;
}
static int ltc4015_get_die_temp(struct ltc4162l_info *info,
union power_supply_propval *val)
{
unsigned int regval;
int ret;
ret = regmap_read(info->regmap, LTC4162L_DIE_TEMPERATURE, &regval);
if (ret)
return ret;
/* (die_temp - 12010) / 45.6°C */
ret = (s16)(regval & 0xFFFF);
ret -= 12010;
ret *= 1000;
ret /= 456;
val->intval = ret;
return 0;
}
static int ltc4162l_get_term_current(struct ltc4162l_info *info,
union power_supply_propval *val)
{
const struct ltc4162l_chip_info *chip_info = info->chip_info;
unsigned int regval;
int ret;
@ -457,10 +723,9 @@ static int ltc4162l_get_term_current(struct ltc4162l_info *info,
if (ret)
return ret;
/* 1.466μV / RSNSB amperes/LSB */
regval *= 14660u;
regval *= chip_info->ibat_resolution_pv;
regval /= info->rsnsb;
val->intval = 100 * regval;
val->intval = regval;
return 0;
}
@ -534,10 +799,11 @@ static ssize_t vbat_show(struct device *dev,
{
struct power_supply *psy = to_power_supply(dev);
struct ltc4162l_info *info = power_supply_get_drvdata(psy);
const struct ltc4162l_chip_info *chip_info = info->chip_info;
union power_supply_propval val;
int ret;
ret = ltc4162l_get_vbat(info, LTC4162L_VBAT, &val);
ret = chip_info->get_vbat(info, LTC4162L_VBAT, &val);
if (ret)
return ret;
@ -550,10 +816,11 @@ static ssize_t vbat_avg_show(struct device *dev,
{
struct power_supply *psy = to_power_supply(dev);
struct ltc4162l_info *info = power_supply_get_drvdata(psy);
const struct ltc4162l_chip_info *chip_info = info->chip_info;
union power_supply_propval val;
int ret;
ret = ltc4162l_get_vbat(info, LTC4162L_VBAT_FILT, &val);
ret = chip_info->get_vbat(info, LTC4162L_VBAT_FILT, &val);
if (ret)
return ret;
@ -589,7 +856,8 @@ static ssize_t force_telemetry_show(struct device *dev,
if (ret)
return ret;
return sysfs_emit(buf, "%u\n", regval & BIT(2) ? 1 : 0);
return sysfs_emit(buf, "%u\n", regval &
info->chip_info->telemetry_mask ? 1 : 0);
}
static ssize_t force_telemetry_store(struct device *dev,
@ -607,7 +875,8 @@ static ssize_t force_telemetry_store(struct device *dev,
return ret;
ret = regmap_update_bits(info->regmap, LTC4162L_CONFIG_BITS_REG,
BIT(2), value ? BIT(2) : 0);
info->chip_info->telemetry_mask,
value ? info->chip_info->telemetry_mask : 0);
if (ret < 0)
return ret;
@ -681,6 +950,7 @@ static int ltc4162l_get_property(struct power_supply *psy,
union power_supply_propval *val)
{
struct ltc4162l_info *info = power_supply_get_drvdata(psy);
const struct ltc4162l_chip_info *chip_info = info->chip_info;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@ -702,15 +972,13 @@ static int ltc4162l_get_property(struct power_supply *psy,
return ltc4162l_get_icharge(info,
LTC4162L_CHARGE_CURRENT_SETTING, val);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
return ltc4162l_get_vcharge(info,
LTC4162L_VCHARGE_DAC, val);
return chip_info->get_vcharge(info, LTC4162L_VCHARGE_DAC, val);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
return ltc4162l_get_vcharge(info,
LTC4162L_VCHARGE_SETTING, val);
return chip_info->get_vcharge(info, LTC4162L_VCHARGE_SETTING, val);
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return ltc4162l_get_iin_limit_dac(info, val);
case POWER_SUPPLY_PROP_TEMP:
return ltc4162l_get_die_temp(info, val);
return chip_info->get_die_temp(info, val);
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return ltc4162l_get_term_current(info, val);
default:
@ -772,7 +1040,6 @@ static enum power_supply_property ltc4162l_properties[] = {
};
static const struct power_supply_desc ltc4162l_desc = {
.name = "ltc4162-l",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = ltc4162l_properties,
.num_properties = ARRAY_SIZE(ltc4162l_properties),
@ -781,6 +1048,50 @@ static const struct power_supply_desc ltc4162l_desc = {
.property_is_writeable = ltc4162l_property_is_writeable,
};
static const struct ltc4162l_chip_info ltc4162l_chip_info = {
.name = "ltc4162-l",
.get_vbat = ltc4162l_get_vbat,
.get_vcharge = ltc4162l_get_vcharge,
.set_vcharge = ltc4162l_set_vcharge,
.get_die_temp = ltc4162l_get_die_temp,
.ibat_resolution_pv = 1466000,
.vin_resolution_uv = 1649,
.telemetry_mask = BIT(2),
};
static const struct ltc4162l_chip_info ltc4162f_chip_info = {
.name = "ltc4162-f",
.get_vbat = ltc4162l_get_vbat,
.get_vcharge = ltc4162l_get_vcharge,
.set_vcharge = ltc4162l_set_vcharge,
.get_die_temp = ltc4162l_get_die_temp,
.ibat_resolution_pv = 1466000,
.vin_resolution_uv = 1649,
.telemetry_mask = BIT(2),
};
static const struct ltc4162l_chip_info ltc4162s_chip_info = {
.name = "ltc4162-s",
.get_vbat = ltc4162l_get_vbat,
.get_vcharge = ltc4162l_get_vcharge,
.set_vcharge = ltc4162l_set_vcharge,
.get_die_temp = ltc4162l_get_die_temp,
.ibat_resolution_pv = 1466000,
.vin_resolution_uv = 1649,
.telemetry_mask = BIT(2),
};
static const struct ltc4162l_chip_info ltc4015_chip_info = {
.name = "ltc4015",
.get_vbat = ltc4015_get_vbat,
.get_vcharge = ltc4015_get_vcharge,
.set_vcharge = ltc4015_set_vcharge,
.get_die_temp = ltc4015_get_die_temp,
.ibat_resolution_pv = 1464870,
.vin_resolution_uv = 1648,
.telemetry_mask = BIT(4),
};
static bool ltc4162l_is_writeable_reg(struct device *dev, unsigned int reg)
{
/* all registers up to this one are writeable */
@ -825,6 +1136,8 @@ static int ltc4162l_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct ltc4162l_info *info;
struct power_supply_config ltc4162l_config = {};
struct power_supply_desc *desc;
const struct ltc4162l_chip_info *chip_info;
u32 value;
int ret;
@ -839,6 +1152,12 @@ static int ltc4162l_probe(struct i2c_client *client)
info->client = client;
i2c_set_clientdata(client, info);
chip_info = i2c_get_match_data(client);
if (!chip_info)
return -ENODEV;
info->chip_info = chip_info;
info->regmap = devm_regmap_init_i2c(client, &ltc4162l_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(dev, "Failed to initialize register map\n");
@ -870,8 +1189,15 @@ static int ltc4162l_probe(struct i2c_client *client)
ltc4162l_config.drv_data = info;
ltc4162l_config.attr_grp = ltc4162l_attr_groups;
info->charger = devm_power_supply_register(dev, &ltc4162l_desc,
&ltc4162l_config);
/* Duplicate the default descriptor to set name based on chip_info. */
desc = devm_kmemdup(dev, &ltc4162l_desc,
sizeof(struct power_supply_desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->name = chip_info->name;
info->charger = devm_power_supply_register(dev, desc, &ltc4162l_config);
if (IS_ERR(info->charger)) {
dev_err(dev, "Failed to register charger\n");
return PTR_ERR(info->charger);
@ -903,14 +1229,20 @@ static void ltc4162l_alert(struct i2c_client *client,
}
static const struct i2c_device_id ltc4162l_i2c_id_table[] = {
{ "ltc4162-l" },
{ "ltc4015", (kernel_ulong_t)&ltc4015_chip_info },
{ "ltc4162-f", (kernel_ulong_t)&ltc4162f_chip_info },
{ "ltc4162-l", (kernel_ulong_t)&ltc4162l_chip_info },
{ "ltc4162-s", (kernel_ulong_t)&ltc4162s_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table);
static const struct of_device_id ltc4162l_of_match[] __maybe_unused = {
{ .compatible = "lltc,ltc4162-l", },
{ },
{ .compatible = "lltc,ltc4015", .data = &ltc4015_chip_info },
{ .compatible = "lltc,ltc4162-f", .data = &ltc4162f_chip_info },
{ .compatible = "lltc,ltc4162-l", .data = &ltc4162l_chip_info },
{ .compatible = "lltc,ltc4162-s", .data = &ltc4162s_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ltc4162l_of_match);

View File

@ -29,6 +29,7 @@
#define MAX172XX_TEMP 0x08 /* Temperature */
#define MAX172XX_CURRENT 0x0A /* Actual current */
#define MAX172XX_AVG_CURRENT 0x0B /* Average current */
#define MAX172XX_FULL_CAP 0x10 /* Calculated full capacity */
#define MAX172XX_TTE 0x11 /* Time to empty */
#define MAX172XX_AVG_TA 0x16 /* Average temperature */
#define MAX172XX_CYCLES 0x17
@ -250,6 +251,7 @@ static const enum power_supply_property max1720x_battery_props[] = {
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
@ -362,6 +364,10 @@ static int max1720x_battery_get_property(struct power_supply *psy,
ret = regmap_read(info->regmap, MAX172XX_AVG_CURRENT, &reg_val);
val->intval = max172xx_current_to_voltage(reg_val) / info->rsense;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
ret = regmap_read(info->regmap, MAX172XX_FULL_CAP, &reg_val);
val->intval = max172xx_capacity_to_ps(reg_val);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
ret = regmap_read(info->regmap, MAX172XX_DEV_NAME, &reg_val);
reg_val = FIELD_GET(MAX172XX_DEV_NAME_TYPE_MASK, reg_val);

View File

@ -90,7 +90,7 @@ static int mm8013_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct mm8013_chip *chip = psy->drv_data;
struct mm8013_chip *chip = power_supply_get_drvdata(psy);
int ret = 0;
u32 regval;

View File

@ -527,7 +527,7 @@ static enum power_supply_property olpc_xo15_bat_props[] = {
#define EEPROM_SIZE (EEPROM_END - EEPROM_START)
static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, size_t count)
const struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
uint8_t ec_byte;
int ret;
@ -547,13 +547,13 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
return count;
}
static struct bin_attribute olpc_bat_eeprom = {
static const struct bin_attribute olpc_bat_eeprom = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
},
.size = EEPROM_SIZE,
.read = olpc_bat_eeprom_read,
.read_new = olpc_bat_eeprom_read,
};
/* Allow userspace to see the specific error value pulled from the EC */
@ -584,15 +584,14 @@ static struct attribute *olpc_bat_sysfs_attrs[] = {
NULL
};
static struct bin_attribute *olpc_bat_sysfs_bin_attrs[] = {
static const struct bin_attribute *const olpc_bat_sysfs_bin_attrs[] = {
&olpc_bat_eeprom,
NULL
};
static const struct attribute_group olpc_bat_sysfs_group = {
.attrs = olpc_bat_sysfs_attrs,
.bin_attrs = olpc_bat_sysfs_bin_attrs,
.bin_attrs_new = olpc_bat_sysfs_bin_attrs,
};
static const struct attribute_group *olpc_bat_sysfs_groups[] = {

View File

@ -9,24 +9,55 @@
* Modified: 2004, Oct Szabolcs Gyurko
*/
#include <linux/lockdep.h>
struct device;
struct device_type;
struct power_supply;
extern int power_supply_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp);
extern bool power_supply_has_property(struct power_supply *psy,
enum power_supply_property psp);
extern bool power_supply_ext_has_property(const struct power_supply_ext *ext,
enum power_supply_property psp);
struct power_supply_ext_registration {
struct list_head list_head;
const struct power_supply_ext *ext;
struct device *dev;
void *data;
};
/* Make sure that the macro is a single expression */
#define power_supply_for_each_extension(pos, psy) \
if ( ({ lockdep_assert_held(&(psy)->extensions_sem); 0; }) ) \
; \
else \
list_for_each_entry(pos, &(psy)->extensions, list_head) \
#ifdef CONFIG_SYSFS
extern void __init power_supply_init_attrs(void);
extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env);
extern const struct attribute_group *power_supply_attr_groups[];
extern int power_supply_sysfs_add_extension(struct power_supply *psy,
const struct power_supply_ext *ext,
struct device *dev);
extern void power_supply_sysfs_remove_extension(struct power_supply *psy,
const struct power_supply_ext *ext);
#else
static inline void power_supply_init_attrs(void) {}
#define power_supply_attr_groups NULL
#define power_supply_uevent NULL
static inline int power_supply_sysfs_add_extension(struct power_supply *psy,
const struct power_supply_ext *ext,
struct device *dev)
{ return 0; }
static inline void power_supply_sysfs_remove_extension(struct power_supply *psy,
const struct power_supply_ext *ext) {}
#endif /* CONFIG_SYSFS */

View File

@ -66,21 +66,19 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier,
return false;
}
static int __power_supply_changed_work(struct device *dev, void *data)
static int __power_supply_changed_work(struct power_supply *pst, void *data)
{
struct power_supply *psy = data;
struct power_supply *pst = dev_get_drvdata(dev);
if (__power_supply_is_supplied_by(psy, pst)) {
if (pst->desc->external_power_changed)
pst->desc->external_power_changed(pst);
}
if (__power_supply_is_supplied_by(psy, pst))
power_supply_external_power_changed(pst);
return 0;
}
static void power_supply_changed_work(struct work_struct *work)
{
int ret;
unsigned long flags;
struct power_supply *psy = container_of(work, struct power_supply,
changed_work);
@ -88,6 +86,16 @@ static void power_supply_changed_work(struct work_struct *work)
dev_dbg(&psy->dev, "%s\n", __func__);
spin_lock_irqsave(&psy->changed_lock, flags);
if (unlikely(psy->update_groups)) {
psy->update_groups = false;
spin_unlock_irqrestore(&psy->changed_lock, flags);
ret = sysfs_update_groups(&psy->dev.kobj, power_supply_dev_type.groups);
if (ret)
dev_warn(&psy->dev, "failed to update sysfs groups: %pe\n", ERR_PTR(ret));
spin_lock_irqsave(&psy->changed_lock, flags);
}
/*
* Check 'changed' here to avoid issues due to race between
* power_supply_changed() and this routine. In worst case
@ -98,7 +106,7 @@ static void power_supply_changed_work(struct work_struct *work)
if (likely(psy->changed)) {
psy->changed = false;
spin_unlock_irqrestore(&psy->changed_lock, flags);
power_supply_for_each_device(psy, __power_supply_changed_work);
power_supply_for_each_psy(psy, __power_supply_changed_work);
power_supply_update_leds(psy);
blocking_notifier_call_chain(&power_supply_notifier,
PSY_EVENT_PROP_CHANGED, psy);
@ -116,11 +124,29 @@ static void power_supply_changed_work(struct work_struct *work)
spin_unlock_irqrestore(&psy->changed_lock, flags);
}
int power_supply_for_each_device(void *data, int (*fn)(struct device *dev, void *data))
struct psy_for_each_psy_cb_data {
int (*fn)(struct power_supply *psy, void *data);
void *data;
};
static int psy_for_each_psy_cb(struct device *dev, void *data)
{
return class_for_each_device(&power_supply_class, NULL, data, fn);
struct psy_for_each_psy_cb_data *cb_data = data;
struct power_supply *psy = dev_to_psy(dev);
return cb_data->fn(psy, cb_data->data);
}
EXPORT_SYMBOL_GPL(power_supply_for_each_device);
int power_supply_for_each_psy(void *data, int (*fn)(struct power_supply *psy, void *data))
{
struct psy_for_each_psy_cb_data cb_data = {
.fn = fn,
.data = data,
};
return class_for_each_device(&power_supply_class, NULL, &cb_data, psy_for_each_psy_cb);
}
EXPORT_SYMBOL_GPL(power_supply_for_each_psy);
void power_supply_changed(struct power_supply *psy)
{
@ -166,11 +192,10 @@ static void power_supply_deferred_register_work(struct work_struct *work)
}
#ifdef CONFIG_OF
static int __power_supply_populate_supplied_from(struct device *dev,
static int __power_supply_populate_supplied_from(struct power_supply *epsy,
void *data)
{
struct power_supply *psy = data;
struct power_supply *epsy = dev_get_drvdata(dev);
struct device_node *np;
int i = 0;
@ -197,20 +222,19 @@ static int power_supply_populate_supplied_from(struct power_supply *psy)
{
int error;
error = power_supply_for_each_device(psy, __power_supply_populate_supplied_from);
error = power_supply_for_each_psy(psy, __power_supply_populate_supplied_from);
dev_dbg(&psy->dev, "%s %d\n", __func__, error);
return error;
}
static int __power_supply_find_supply_from_node(struct device *dev,
static int __power_supply_find_supply_from_node(struct power_supply *epsy,
void *data)
{
struct device_node *np = data;
struct power_supply *epsy = dev_get_drvdata(dev);
/* returning non-zero breaks out of power_supply_for_each_device loop */
/* returning non-zero breaks out of power_supply_for_each_psy loop */
if (epsy->of_node == np)
return 1;
@ -222,16 +246,16 @@ static int power_supply_find_supply_from_node(struct device_node *supply_node)
int error;
/*
* power_supply_for_each_device() either returns its own errors or values
* power_supply_for_each_psy() either returns its own errors or values
* returned by __power_supply_find_supply_from_node().
*
* __power_supply_find_supply_from_node() will return 0 (no match)
* or 1 (match).
*
* We return 0 if power_supply_for_each_device() returned 1, -EPROBE_DEFER if
* We return 0 if power_supply_for_each_psy() returned 1, -EPROBE_DEFER if
* it returned 0, or error as returned by it.
*/
error = power_supply_for_each_device(supply_node, __power_supply_find_supply_from_node);
error = power_supply_for_each_psy(supply_node, __power_supply_find_supply_from_node);
return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
}
@ -316,10 +340,9 @@ struct psy_am_i_supplied_data {
unsigned int count;
};
static int __power_supply_am_i_supplied(struct device *dev, void *_data)
static int __power_supply_am_i_supplied(struct power_supply *epsy, void *_data)
{
union power_supply_propval ret = {0,};
struct power_supply *epsy = dev_get_drvdata(dev);
struct psy_am_i_supplied_data *data = _data;
if (__power_supply_is_supplied_by(epsy, data->psy)) {
@ -337,7 +360,7 @@ int power_supply_am_i_supplied(struct power_supply *psy)
struct psy_am_i_supplied_data data = { psy, 0 };
int error;
error = power_supply_for_each_device(&data, __power_supply_am_i_supplied);
error = power_supply_for_each_psy(&data, __power_supply_am_i_supplied);
dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error);
@ -348,10 +371,9 @@ int power_supply_am_i_supplied(struct power_supply *psy)
}
EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
static int __power_supply_is_system_supplied(struct device *dev, void *data)
static int __power_supply_is_system_supplied(struct power_supply *psy, void *data)
{
union power_supply_propval ret = {0,};
struct power_supply *psy = dev_get_drvdata(dev);
unsigned int *count = data;
if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_SCOPE, &ret))
@ -372,7 +394,7 @@ int power_supply_is_system_supplied(void)
int error;
unsigned int count = 0;
error = power_supply_for_each_device(&count, __power_supply_is_system_supplied);
error = power_supply_for_each_psy(&count, __power_supply_is_system_supplied);
/*
* If no system scope power class device was found at all, most probably we
@ -391,9 +413,8 @@ struct psy_get_supplier_prop_data {
union power_supply_propval *val;
};
static int __power_supply_get_supplier_property(struct device *dev, void *_data)
static int __power_supply_get_supplier_property(struct power_supply *epsy, void *_data)
{
struct power_supply *epsy = dev_get_drvdata(dev);
struct psy_get_supplier_prop_data *data = _data;
if (__power_supply_is_supplied_by(epsy, data->psy))
@ -418,7 +439,7 @@ int power_supply_get_property_from_supplier(struct power_supply *psy,
* This function is not intended for use with a supply with multiple
* suppliers, we simply pick the first supply to report the psp.
*/
ret = power_supply_for_each_device(&data, __power_supply_get_supplier_property);
ret = power_supply_for_each_psy(&data, __power_supply_get_supplier_property);
if (ret < 0)
return ret;
if (ret == 0)
@ -444,7 +465,7 @@ EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
static int power_supply_match_device_by_name(struct device *dev, const void *data)
{
const char *name = data;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
return strcmp(psy->desc->name, name) == 0;
}
@ -467,7 +488,7 @@ struct power_supply *power_supply_get_by_name(const char *name)
power_supply_match_device_by_name);
if (dev) {
psy = dev_get_drvdata(dev);
psy = dev_to_psy(dev);
atomic_inc(&psy->use_cnt);
}
@ -524,7 +545,7 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
of_node_put(power_supply_np);
if (dev) {
psy = dev_get_drvdata(dev);
psy = dev_to_psy(dev);
atomic_inc(&psy->use_cnt);
}
@ -1180,8 +1201,8 @@ bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info,
}
EXPORT_SYMBOL_GPL(power_supply_battery_bti_in_range);
static bool psy_has_property(const struct power_supply_desc *psy_desc,
enum power_supply_property psp)
static bool psy_desc_has_property(const struct power_supply_desc *psy_desc,
enum power_supply_property psp)
{
bool found = false;
int i;
@ -1196,17 +1217,57 @@ static bool psy_has_property(const struct power_supply_desc *psy_desc,
return found;
}
bool power_supply_ext_has_property(const struct power_supply_ext *psy_ext,
enum power_supply_property psp)
{
int i;
for (i = 0; i < psy_ext->num_properties; i++)
if (psy_ext->properties[i] == psp)
return true;
return false;
}
bool power_supply_has_property(struct power_supply *psy,
enum power_supply_property psp)
{
struct power_supply_ext_registration *reg;
if (psy_desc_has_property(psy->desc, psp))
return true;
if (power_supply_battery_info_has_prop(psy->battery_info, psp))
return true;
power_supply_for_each_extension(reg, psy) {
if (power_supply_ext_has_property(reg->ext, psp))
return true;
}
return false;
}
int power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct power_supply_ext_registration *reg;
if (atomic_read(&psy->use_cnt) <= 0) {
if (!psy->initialized)
return -EAGAIN;
return -ENODEV;
}
if (psy_has_property(psy->desc, psp))
scoped_guard(rwsem_read, &psy->extensions_sem) {
power_supply_for_each_extension(reg, psy) {
if (power_supply_ext_has_property(reg->ext, psp))
return reg->ext->get_property(psy, reg->ext, reg->data, psp, val);
}
}
if (psy_desc_has_property(psy->desc, psp))
return psy->desc->get_property(psy, psp, val);
else if (power_supply_battery_info_has_prop(psy->battery_info, psp))
return power_supply_battery_info_get_prop(psy->battery_info, psp, val);
@ -1219,7 +1280,24 @@ int power_supply_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property)
struct power_supply_ext_registration *reg;
if (atomic_read(&psy->use_cnt) <= 0)
return -ENODEV;
scoped_guard(rwsem_read, &psy->extensions_sem) {
power_supply_for_each_extension(reg, psy) {
if (power_supply_ext_has_property(reg->ext, psp)) {
if (reg->ext->set_property)
return reg->ext->set_property(psy, reg->ext, reg->data,
psp, val);
else
return -ENODEV;
}
}
}
if (!psy->desc->set_property)
return -ENODEV;
return psy->desc->set_property(psy, psp, val);
@ -1229,7 +1307,22 @@ EXPORT_SYMBOL_GPL(power_supply_set_property);
int power_supply_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
return psy->desc->property_is_writeable && psy->desc->property_is_writeable(psy, psp);
struct power_supply_ext_registration *reg;
power_supply_for_each_extension(reg, psy) {
if (power_supply_ext_has_property(reg->ext, psp)) {
if (reg->ext->property_is_writeable)
return reg->ext->property_is_writeable(psy, reg->ext,
reg->data, psp);
else
return 0;
}
}
if (!psy->desc->property_is_writeable)
return 0;
return psy->desc->property_is_writeable(psy, psp);
}
void power_supply_external_power_changed(struct power_supply *psy)
@ -1248,6 +1341,88 @@ int power_supply_powers(struct power_supply *psy, struct device *dev)
}
EXPORT_SYMBOL_GPL(power_supply_powers);
static int power_supply_update_sysfs_and_hwmon(struct power_supply *psy)
{
unsigned long flags;
spin_lock_irqsave(&psy->changed_lock, flags);
psy->update_groups = true;
spin_unlock_irqrestore(&psy->changed_lock, flags);
power_supply_changed(psy);
power_supply_remove_hwmon_sysfs(psy);
return power_supply_add_hwmon_sysfs(psy);
}
int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext,
struct device *dev, void *data)
{
struct power_supply_ext_registration *reg;
size_t i;
int ret;
if (!psy || !dev || !ext || !ext->name || !ext->properties || !ext->num_properties)
return -EINVAL;
guard(rwsem_write)(&psy->extensions_sem);
power_supply_for_each_extension(reg, psy)
if (strcmp(ext->name, reg->ext->name) == 0)
return -EEXIST;
for (i = 0; i < ext->num_properties; i++)
if (power_supply_has_property(psy, ext->properties[i]))
return -EEXIST;
reg = kmalloc(sizeof(*reg), GFP_KERNEL);
if (!reg)
return -ENOMEM;
reg->ext = ext;
reg->dev = dev;
reg->data = data;
list_add(&reg->list_head, &psy->extensions);
ret = power_supply_sysfs_add_extension(psy, ext, dev);
if (ret)
goto sysfs_add_failed;
ret = power_supply_update_sysfs_and_hwmon(psy);
if (ret)
goto sysfs_hwmon_failed;
return 0;
sysfs_hwmon_failed:
power_supply_sysfs_remove_extension(psy, ext);
sysfs_add_failed:
list_del(&reg->list_head);
kfree(reg);
return ret;
}
EXPORT_SYMBOL_GPL(power_supply_register_extension);
void power_supply_unregister_extension(struct power_supply *psy, const struct power_supply_ext *ext)
{
struct power_supply_ext_registration *reg;
guard(rwsem_write)(&psy->extensions_sem);
power_supply_for_each_extension(reg, psy) {
if (reg->ext == ext) {
list_del(&reg->list_head);
power_supply_sysfs_remove_extension(psy, ext);
kfree(reg);
power_supply_update_sysfs_and_hwmon(psy);
return;
}
}
dev_warn(&psy->dev, "Trying to unregister invalid extension");
}
EXPORT_SYMBOL_GPL(power_supply_unregister_extension);
static void power_supply_dev_release(struct device *dev)
{
struct power_supply *psy = to_power_supply(dev);
@ -1300,7 +1475,7 @@ static int psy_register_thermal(struct power_supply *psy)
return 0;
/* Register battery zone device psy reports temperature */
if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_TEMP)) {
if (psy_desc_has_property(psy->desc, POWER_SUPPLY_PROP_TEMP)) {
/* Prefer our hwmon device and avoid duplicates */
struct thermal_zone_params tzp = {
.no_hwmon = IS_ENABLED(CONFIG_POWER_SUPPLY_HWMON)
@ -1402,6 +1577,9 @@ __power_supply_register(struct device *parent,
}
spin_lock_init(&psy->changed_lock);
init_rwsem(&psy->extensions_sem);
INIT_LIST_HEAD(&psy->extensions);
rc = device_add(dev);
if (rc)
goto device_add_failed;
@ -1414,13 +1592,15 @@ __power_supply_register(struct device *parent,
if (rc)
goto register_thermal_failed;
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
scoped_guard(rwsem_read, &psy->extensions_sem) {
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
rc = power_supply_add_hwmon_sysfs(psy);
if (rc)
goto add_hwmon_sysfs_failed;
rc = power_supply_add_hwmon_sysfs(psy);
if (rc)
goto add_hwmon_sysfs_failed;
}
/*
* Update use_cnt after any uevents (most notably from device_add()).

View File

@ -349,9 +349,28 @@ static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
.info = power_supply_hwmon_info,
};
static const enum power_supply_property power_supply_hwmon_props[] = {
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MAX,
POWER_SUPPLY_PROP_TEMP_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
int power_supply_add_hwmon_sysfs(struct power_supply *psy)
{
const struct power_supply_desc *desc = psy->desc;
struct power_supply_hwmon *psyhw;
struct device *dev = &psy->dev;
struct device *hwmon;
@ -377,32 +396,11 @@ int power_supply_add_hwmon_sysfs(struct power_supply *psy)
goto error;
}
for (i = 0; i < desc->num_properties; i++) {
const enum power_supply_property prop = desc->properties[i];
for (i = 0; i < ARRAY_SIZE(power_supply_hwmon_props); i++) {
const enum power_supply_property prop = power_supply_hwmon_props[i];
switch (prop) {
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_POWER_AVG:
case POWER_SUPPLY_PROP_POWER_NOW:
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TEMP_MAX:
case POWER_SUPPLY_PROP_TEMP_MIN:
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (power_supply_has_property(psy, prop))
set_bit(prop, psyhw->props);
break;
default:
break;
}
}
name = psy->desc->name;

View File

@ -182,6 +182,8 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = {
POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),
POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR),
/* Same enum value texts as "charge_type" without the 's' at the end */
_POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPES, POWER_SUPPLY_CHARGE_TYPE_TEXT),
POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),
POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),
POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),
@ -237,23 +239,52 @@ static enum power_supply_property dev_attr_psp(struct device_attribute *attr)
return to_ps_attr(attr) - power_supply_attrs;
}
static void power_supply_escape_spaces(const char *str, char *buf, size_t bufsize)
{
strscpy(buf, str, bufsize);
strreplace(buf, ' ', '_');
}
static int power_supply_match_string(const char * const *array, size_t n, const char *s)
{
int ret;
/* First try an exact match */
ret = __sysfs_match_string(array, n, s);
if (ret >= 0)
return ret;
/* Second round, try matching with spaces replaced by '_' */
for (size_t i = 0; i < n; i++) {
char buf[32];
power_supply_escape_spaces(array[i], buf, sizeof(buf));
if (sysfs_streq(buf, s))
return i;
}
return -EINVAL;
}
static ssize_t power_supply_show_enum_with_available(
struct device *dev, const char * const labels[], int label_count,
unsigned int available_values, int value, char *buf)
{
bool match = false, available, active;
char escaped_label[32];
ssize_t count = 0;
int i;
for (i = 0; i < label_count; i++) {
available = available_values & BIT(i);
active = i == value;
power_supply_escape_spaces(labels[i], escaped_label, sizeof(escaped_label));
if (available && active) {
count += sysfs_emit_at(buf, count, "[%s] ", labels[i]);
count += sysfs_emit_at(buf, count, "[%s] ", escaped_label);
match = true;
} else if (available) {
count += sysfs_emit_at(buf, count, "%s ", labels[i]);
count += sysfs_emit_at(buf, count, "%s ", escaped_label);
}
}
@ -268,11 +299,34 @@ static ssize_t power_supply_show_enum_with_available(
return count;
}
static ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf) {
static ssize_t power_supply_show_charge_behaviour(struct device *dev,
struct power_supply *psy,
union power_supply_propval *value,
char *buf)
{
struct power_supply_ext_registration *reg;
scoped_guard(rwsem_read, &psy->extensions_sem) {
power_supply_for_each_extension(reg, psy) {
if (power_supply_ext_has_property(reg->ext,
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR))
return power_supply_charge_behaviour_show(dev,
reg->ext->charge_behaviours,
value->intval, buf);
}
}
return power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours,
value->intval, buf);
}
static ssize_t power_supply_format_property(struct device *dev,
bool uevent,
struct device_attribute *attr,
char *buf)
{
ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
const struct power_supply_attr *ps_attr = to_ps_attr(attr);
enum power_supply_property psp = dev_attr_psp(attr);
union power_supply_propval value;
@ -287,7 +341,7 @@ static ssize_t power_supply_show_property(struct device *dev,
dev_dbg_ratelimited(dev,
"driver has no data for `%s' property\n",
attr->attr.name);
else if (ret != -ENODEV && ret != -EAGAIN)
else if (ret != -ENODEV && ret != -EAGAIN && ret != -EINVAL)
dev_err_ratelimited(dev,
"driver failed to report `%s' property: %zd\n",
attr->attr.name, ret);
@ -303,13 +357,21 @@ static ssize_t power_supply_show_property(struct device *dev,
psy->desc->usb_types, value.intval, buf);
break;
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
ret = power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours,
value.intval, buf);
if (uevent) /* no possible values in uevents */
goto default_format;
ret = power_supply_show_charge_behaviour(dev, psy, &value, buf);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPES:
if (uevent) /* no possible values in uevents */
goto default_format;
ret = power_supply_charge_types_show(dev, psy->desc->charge_types,
value.intval, buf);
break;
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = sysfs_emit(buf, "%s\n", value.strval);
break;
default:
default_format:
if (ps_attr->text_values_len > 0 &&
value.intval < ps_attr->text_values_len && value.intval >= 0) {
ret = sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]);
@ -321,19 +383,26 @@ static ssize_t power_supply_show_property(struct device *dev,
return ret;
}
static ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return power_supply_format_property(dev, false, attr, buf);
}
static ssize_t power_supply_store_property(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count) {
ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
const struct power_supply_attr *ps_attr = to_ps_attr(attr);
enum power_supply_property psp = dev_attr_psp(attr);
union power_supply_propval value;
ret = -EINVAL;
if (ps_attr->text_values_len > 0) {
ret = __sysfs_match_string(ps_attr->text_values,
ps_attr->text_values_len, buf);
ret = power_supply_match_string(ps_attr->text_values,
ps_attr->text_values_len, buf);
}
/*
@ -364,9 +433,8 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
int attrno)
{
struct device *dev = kobj_to_dev(kobj);
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
int i;
if (!power_supply_attrs[attrno].prop_name)
return 0;
@ -374,19 +442,13 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
if (attrno == POWER_SUPPLY_PROP_TYPE)
return mode;
for (i = 0; i < psy->desc->num_properties; i++) {
int property = psy->desc->properties[i];
guard(rwsem_read)(&psy->extensions_sem);
if (property == attrno) {
if (power_supply_property_is_writeable(psy, property) > 0)
mode |= S_IWUSR;
return mode;
}
}
if (power_supply_battery_info_has_prop(psy->battery_info, attrno))
if (power_supply_has_property(psy, attrno)) {
if (power_supply_property_is_writeable(psy, attrno) > 0)
mode |= S_IWUSR;
return mode;
}
return 0;
}
@ -396,8 +458,18 @@ static const struct attribute_group power_supply_attr_group = {
.is_visible = power_supply_attr_is_visible,
};
static struct attribute *power_supply_extension_attrs[] = {
NULL
};
static const struct attribute_group power_supply_extension_group = {
.name = "extensions",
.attrs = power_supply_extension_attrs,
};
const struct attribute_group *power_supply_attr_groups[] = {
&power_supply_attr_group,
&power_supply_extension_group,
NULL
};
@ -437,8 +509,8 @@ static int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env
pwr_attr = &power_supply_attrs[prop];
dev_attr = &pwr_attr->dev_attr;
ret = power_supply_show_property((struct device *)dev, dev_attr, prop_buf);
if (ret == -ENODEV || ret == -ENODATA) {
ret = power_supply_format_property((struct device *)dev, true, dev_attr, prop_buf);
if (ret == -ENODEV || ret == -ENODATA || ret == -EINVAL) {
/*
* When a battery is absent, we expect -ENODEV. Don't abort;
* send the uevent with at least the PRESENT=0 property
@ -459,11 +531,7 @@ static int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env
int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const struct power_supply *psy = dev_get_drvdata(dev);
const enum power_supply_property *battery_props =
power_supply_battery_info_properties;
unsigned long psy_drv_properties[POWER_SUPPLY_ATTR_CNT /
sizeof(unsigned long) + 1] = {0};
const struct power_supply *psy = dev_to_psy(dev);
int ret = 0, j;
char *prop_buf;
@ -491,22 +559,8 @@ int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env)
if (ret)
goto out;
for (j = 0; j < psy->desc->num_properties; j++) {
set_bit(psy->desc->properties[j], psy_drv_properties);
ret = add_prop_uevent(dev, env, psy->desc->properties[j],
prop_buf);
if (ret)
goto out;
}
for (j = 0; j < power_supply_battery_info_properties_size; j++) {
if (test_bit(battery_props[j], psy_drv_properties))
continue;
if (!power_supply_battery_info_has_prop(psy->battery_info,
battery_props[j]))
continue;
ret = add_prop_uevent(dev, env, battery_props[j],
prop_buf);
for (j = 0; j < POWER_SUPPLY_ATTR_CNT; j++) {
ret = add_prop_uevent(dev, env, j, prop_buf);
if (ret)
goto out;
}
@ -542,3 +596,44 @@ int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const
return -EINVAL;
}
EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse);
ssize_t power_supply_charge_types_show(struct device *dev,
unsigned int available_types,
enum power_supply_charge_type current_type,
char *buf)
{
return power_supply_show_enum_with_available(
dev, POWER_SUPPLY_CHARGE_TYPE_TEXT,
ARRAY_SIZE(POWER_SUPPLY_CHARGE_TYPE_TEXT),
available_types, current_type, buf);
}
EXPORT_SYMBOL_GPL(power_supply_charge_types_show);
int power_supply_charge_types_parse(unsigned int available_types, const char *buf)
{
int i = power_supply_match_string(POWER_SUPPLY_CHARGE_TYPE_TEXT,
ARRAY_SIZE(POWER_SUPPLY_CHARGE_TYPE_TEXT),
buf);
if (i < 0)
return i;
if (available_types & BIT(i))
return i;
return -EINVAL;
}
EXPORT_SYMBOL_GPL(power_supply_charge_types_parse);
int power_supply_sysfs_add_extension(struct power_supply *psy, const struct power_supply_ext *ext,
struct device *dev)
{
return sysfs_add_link_to_group(&psy->dev.kobj, power_supply_extension_group.name,
&dev->kobj, ext->name);
}
void power_supply_sysfs_remove_extension(struct power_supply *psy,
const struct power_supply_ext *ext)
{
sysfs_remove_link_from_group(&psy->dev.kobj, power_supply_extension_group.name, ext->name);
}

View File

@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0
/*
* stc3117_fuel_gauge.c - STMicroelectronics STC3117 Fuel Gauge Driver
*
* Copyright (c) 2024 Silicon Signals Pvt Ltd.
* Author: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
* Bhavin Sharma <bhavin.sharma@siliconsignals.io>
*/
#include <linux/crc8.h>
#include <linux/devm-helpers.h>
#include <linux/i2c.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/workqueue.h>
#define STC3117_ADDR_MODE 0x00
#define STC3117_ADDR_CTRL 0x01
#define STC3117_ADDR_SOC_L 0x02
#define STC3117_ADDR_SOC_H 0x03
#define STC3117_ADDR_COUNTER_L 0x04
#define STC3117_ADDR_COUNTER_H 0x05
#define STC3117_ADDR_CURRENT_L 0x06
#define STC3117_ADDR_CURRENT_H 0x07
#define STC3117_ADDR_VOLTAGE_L 0x08
#define STC3117_ADDR_VOLTAGE_H 0x09
#define STC3117_ADDR_TEMPERATURE 0x0A
#define STC3117_ADDR_AVG_CURRENT_L 0x0B
#define STC3117_ADDR_AVG_CURRENT_H 0x0C
#define STC3117_ADDR_OCV_L 0x0D
#define STC3117_ADDR_OCV_H 0x0E
#define STC3117_ADDR_CC_CNF_L 0x0F
#define STC3117_ADDR_CC_CNF_H 0x10
#define STC3117_ADDR_VM_CNF_L 0x11
#define STC3117_ADDR_VM_CNF_H 0x12
#define STC3117_ADDR_ALARM_soc 0x13
#define STC3117_ADDR_ALARM_VOLTAGE 0x14
#define STC3117_ADDR_ID 0x18
#define STC3117_ADDR_CC_ADJ_L 0x1B
#define STC3117_ADDR_CC_ADJ_H 0x1C
#define STC3117_ADDR_VM_ADJ_L 0x1D
#define STC3117_ADDR_VM_ADJ_H 0x1E
#define STC3117_ADDR_RAM 0x20
#define STC3117_ADDR_OCV_TABLE 0x30
#define STC3117_ADDR_SOC_TABLE 0x30
/* Bit mask definition */
#define STC3117_ID 0x16
#define STC3117_MIXED_MODE 0x00
#define STC3117_VMODE BIT(0)
#define STC3117_GG_RUN BIT(4)
#define STC3117_CC_MODE BIT(5)
#define STC3117_BATFAIL BIT(3)
#define STC3117_PORDET BIT(4)
#define STC3117_RAM_SIZE 16
#define STC3117_OCV_TABLE_SIZE 16
#define STC3117_RAM_TESTWORD 0x53A9
#define STC3117_SOFT_RESET 0x11
#define STC3117_NOMINAL_CAPACITY 2600
#define VOLTAGE_LSB_VALUE 9011
#define CURRENT_LSB_VALUE 24084
#define APP_CUTOFF_VOLTAGE 2500
#define MAX_HRSOC 51200
#define MAX_SOC 1000
#define CHG_MIN_CURRENT 200
#define CHG_END_CURRENT 20
#define APP_MIN_CURRENT (-5)
#define BATTERY_FULL 95
#define CRC8_POLYNOMIAL 0x07
#define CRC8_INIT 0x00
DECLARE_CRC8_TABLE(stc3117_crc_table);
enum stc3117_state {
STC3117_INIT,
STC3117_RUNNING,
STC3117_POWERDN,
};
/* Default ocv curve Li-ion battery */
static const int ocv_value[16] = {
3400, 3582, 3669, 3676, 3699, 3737, 3757, 3774,
3804, 3844, 3936, 3984, 4028, 4131, 4246, 4320
};
union stc3117_internal_ram {
u8 ram_bytes[STC3117_RAM_SIZE];
struct {
u16 testword; /* 0-1 Bytes */
u16 hrsoc; /* 2-3 Bytes */
u16 cc_cnf; /* 4-5 Bytes */
u16 vm_cnf; /* 6-7 Bytes */
u8 soc; /* 8 Byte */
u8 state; /* 9 Byte */
u8 unused[5]; /* 10-14 Bytes */
u8 crc; /* 15 Byte */
} reg;
};
struct stc3117_battery_info {
int voltage_min_mv;
int voltage_max_mv;
int battery_capacity_mah;
int sense_resistor;
};
struct stc3117_data {
struct i2c_client *client;
struct regmap *regmap;
struct delayed_work update_work;
struct power_supply *battery;
union stc3117_internal_ram ram_data;
struct stc3117_battery_info battery_info;
u8 soc_tab[16];
int cc_cnf;
int vm_cnf;
int cc_adj;
int vm_adj;
int avg_current;
int avg_voltage;
int batt_current;
int voltage;
int temp;
int soc;
int ocv;
int hrsoc;
int presence;
};
static int stc3117_convert(int value, int factor)
{
value = (value * factor) / 4096;
return value * 1000;
}
static int stc3117_get_battery_data(struct stc3117_data *data)
{
u8 reg_list[16];
u8 data_adjust[4];
int value, mode;
regmap_bulk_read(data->regmap, STC3117_ADDR_MODE,
reg_list, sizeof(reg_list));
/* soc */
value = (reg_list[3] << 8) + reg_list[2];
data->hrsoc = value;
data->soc = (value * 10 + 256) / 512;
/* current in uA*/
value = (reg_list[7] << 8) + reg_list[6];
data->batt_current = stc3117_convert(value,
CURRENT_LSB_VALUE / data->battery_info.sense_resistor);
/* voltage in uV */
value = (reg_list[9] << 8) + reg_list[8];
data->voltage = stc3117_convert(value, VOLTAGE_LSB_VALUE);
/* temp in 1/10 °C */
data->temp = reg_list[10] * 10;
/* Avg current in uA */
value = (reg_list[12] << 8) + reg_list[11];
regmap_read(data->regmap, STC3117_ADDR_MODE, &mode);
if (!(mode & STC3117_VMODE)) {
value = stc3117_convert(value,
CURRENT_LSB_VALUE / data->battery_info.sense_resistor);
value = value / 4;
} else {
value = stc3117_convert(value, 36 * STC3117_NOMINAL_CAPACITY);
}
data->avg_current = value;
/* ocv in uV */
value = (reg_list[14] << 8) + reg_list[13];
value = stc3117_convert(value, VOLTAGE_LSB_VALUE);
value = (value + 2) / 4;
data->ocv = value;
/* CC & VM adjustment counters */
regmap_bulk_read(data->regmap, STC3117_ADDR_CC_ADJ_L,
data_adjust, sizeof(data_adjust));
value = (data_adjust[1] << 8) + data_adjust[0];
data->cc_adj = value;
value = (data_adjust[3] << 8) + data_adjust[2];
data->vm_adj = value;
return 0;
}
static int ram_write(struct stc3117_data *data)
{
int ret;
ret = regmap_bulk_write(data->regmap, STC3117_ADDR_RAM,
data->ram_data.ram_bytes, STC3117_RAM_SIZE);
if (ret)
return ret;
return 0;
};
static int ram_read(struct stc3117_data *data)
{
int ret;
ret = regmap_bulk_read(data->regmap, STC3117_ADDR_RAM,
data->ram_data.ram_bytes, STC3117_RAM_SIZE);
if (ret)
return ret;
return 0;
};
static int stc3117_set_para(struct stc3117_data *data)
{
int ret;
ret = regmap_write(data->regmap, STC3117_ADDR_MODE, STC3117_VMODE);
for (int i = 0; i < STC3117_OCV_TABLE_SIZE; i++)
ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_TABLE + i,
ocv_value[i] * 100 / 55);
if (data->soc_tab[1] != 0)
ret |= regmap_bulk_write(data->regmap, STC3117_ADDR_SOC_TABLE,
data->soc_tab, STC3117_OCV_TABLE_SIZE);
ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_H,
(data->ram_data.reg.cc_cnf >> 8) & 0xFF);
ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_L,
data->ram_data.reg.cc_cnf & 0xFF);
ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_H,
(data->ram_data.reg.vm_cnf >> 8) & 0xFF);
ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_L,
data->ram_data.reg.vm_cnf & 0xFF);
ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, 0x03);
ret |= regmap_write(data->regmap, STC3117_ADDR_MODE,
STC3117_MIXED_MODE | STC3117_GG_RUN);
return ret;
};
static int stc3117_init(struct stc3117_data *data)
{
int id, ret;
int ctrl;
int ocv_m, ocv_l;
regmap_read(data->regmap, STC3117_ADDR_ID, &id);
if (id != STC3117_ID)
return -EINVAL;
data->cc_cnf = (data->battery_info.battery_capacity_mah *
data->battery_info.sense_resistor * 250 + 6194) / 12389;
data->vm_cnf = (data->battery_info.battery_capacity_mah
* 200 * 50 + 24444) / 48889;
/* Battery has not been removed */
data->presence = 1;
/* Read RAM data */
ret = ram_read(data);
if (ret)
return ret;
if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD ||
(crc8(stc3117_crc_table, data->ram_data.ram_bytes,
STC3117_RAM_SIZE, CRC8_INIT)) != 0) {
data->ram_data.reg.testword = STC3117_RAM_TESTWORD;
data->ram_data.reg.cc_cnf = data->cc_cnf;
data->ram_data.reg.vm_cnf = data->vm_cnf;
data->ram_data.reg.crc = crc8(stc3117_crc_table,
data->ram_data.ram_bytes,
STC3117_RAM_SIZE - 1, CRC8_INIT);
ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m);
ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l);
ret |= stc3117_set_para(data);
ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m);
ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l);
if (ret)
return ret;
} else {
ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &ctrl);
if (ret)
return ret;
if ((ctrl & STC3117_BATFAIL) != 0 ||
(ctrl & STC3117_PORDET) != 0) {
ret = regmap_read(data->regmap,
STC3117_ADDR_OCV_H, &ocv_m);
ret |= regmap_read(data->regmap,
STC3117_ADDR_OCV_L, &ocv_l);
ret |= stc3117_set_para(data);
ret |= regmap_write(data->regmap,
STC3117_ADDR_OCV_H, ocv_m);
ret |= regmap_write(data->regmap,
STC3117_ADDR_OCV_L, ocv_l);
if (ret)
return ret;
} else {
ret = stc3117_set_para(data);
ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H,
(data->ram_data.reg.hrsoc >> 8 & 0xFF));
ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L,
(data->ram_data.reg.hrsoc & 0xFF));
if (ret)
return ret;
}
}
data->ram_data.reg.state = STC3117_INIT;
data->ram_data.reg.crc = crc8(stc3117_crc_table,
data->ram_data.ram_bytes,
STC3117_RAM_SIZE - 1, CRC8_INIT);
ret = ram_write(data);
if (ret)
return ret;
return 0;
};
static int stc3117_task(struct stc3117_data *data)
{
int id, mode, ret;
int count_l, count_m;
int ocv_l, ocv_m;
regmap_read(data->regmap, STC3117_ADDR_ID, &id);
if (id != STC3117_ID) {
data->presence = 0;
return -EINVAL;
}
stc3117_get_battery_data(data);
/* Read RAM data */
ret = ram_read(data);
if (ret)
return ret;
if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD ||
(crc8(stc3117_crc_table, data->ram_data.ram_bytes,
STC3117_RAM_SIZE, CRC8_INIT) != 0)) {
data->ram_data.reg.testword = STC3117_RAM_TESTWORD;
data->ram_data.reg.cc_cnf = data->cc_cnf;
data->ram_data.reg.vm_cnf = data->vm_cnf;
data->ram_data.reg.crc = crc8(stc3117_crc_table,
data->ram_data.ram_bytes,
STC3117_RAM_SIZE - 1, CRC8_INIT);
data->ram_data.reg.state = STC3117_INIT;
}
/* check battery presence status */
ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &mode);
if ((mode & STC3117_BATFAIL) != 0) {
data->presence = 0;
data->ram_data.reg.testword = 0;
data->ram_data.reg.state = STC3117_INIT;
ret = ram_write(data);
ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, STC3117_PORDET);
if (ret)
return ret;
}
data->presence = 1;
ret = regmap_read(data->regmap, STC3117_ADDR_MODE, &mode);
if (ret)
return ret;
if ((mode & STC3117_GG_RUN) == 0) {
if (data->ram_data.reg.state > STC3117_INIT) {
ret = stc3117_set_para(data);
ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H,
(data->ram_data.reg.hrsoc >> 8 & 0xFF));
ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L,
(data->ram_data.reg.hrsoc & 0xFF));
if (ret)
return ret;
} else {
ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m);
ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l);
ret |= stc3117_set_para(data);
ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m);
ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l);
if (ret)
return ret;
}
data->ram_data.reg.state = STC3117_INIT;
}
regmap_read(data->regmap, STC3117_ADDR_COUNTER_L, &count_l);
regmap_read(data->regmap, STC3117_ADDR_COUNTER_H, &count_m);
count_m = (count_m << 8) + count_l;
/* INIT state, wait for batt_current & temperature value available: */
if (data->ram_data.reg.state == STC3117_INIT && count_m > 4) {
data->avg_voltage = data->voltage;
data->avg_current = data->batt_current;
data->ram_data.reg.state = STC3117_RUNNING;
}
if (data->ram_data.reg.state != STC3117_RUNNING) {
data->batt_current = -ENODATA;
data->temp = -ENODATA;
} else {
if (data->voltage < APP_CUTOFF_VOLTAGE)
data->soc = -ENODATA;
if (mode & STC3117_VMODE) {
data->avg_current = -ENODATA;
data->batt_current = -ENODATA;
}
}
data->ram_data.reg.hrsoc = data->hrsoc;
data->ram_data.reg.soc = (data->soc + 5) / 10;
data->ram_data.reg.crc = crc8(stc3117_crc_table,
data->ram_data.ram_bytes,
STC3117_RAM_SIZE - 1, CRC8_INIT);
ret = ram_write(data);
if (ret)
return ret;
return 0;
};
static void fuel_gauge_update_work(struct work_struct *work)
{
struct stc3117_data *data =
container_of(work, struct stc3117_data, update_work.work);
stc3117_task(data);
/* Schedule the work to run again in 2 seconds */
schedule_delayed_work(&data->update_work, msecs_to_jiffies(2000));
}
static int stc3117_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
struct stc3117_data *data = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (data->soc > BATTERY_FULL)
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (data->batt_current < 0)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (data->batt_current > 0)
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = data->voltage;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = data->batt_current;
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
val->intval = data->ocv;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
val->intval = data->avg_current;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = data->soc;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = data->temp;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = data->presence;
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property stc3117_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_PRESENT,
};
static const struct power_supply_desc stc3117_battery_desc = {
.name = "stc3117-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = stc3117_get_property,
.properties = stc3117_battery_props,
.num_properties = ARRAY_SIZE(stc3117_battery_props),
};
static const struct regmap_config stc3117_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int stc3117_probe(struct i2c_client *client)
{
struct stc3117_data *data;
struct power_supply_config psy_cfg = {};
struct power_supply_battery_info *info;
int ret;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
data->regmap = devm_regmap_init_i2c(client, &stc3117_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
psy_cfg.drv_data = data;
psy_cfg.fwnode = dev_fwnode(&client->dev);
crc8_populate_msb(stc3117_crc_table, CRC8_POLYNOMIAL);
data->battery = devm_power_supply_register(&client->dev,
&stc3117_battery_desc, &psy_cfg);
if (IS_ERR(data->battery))
return dev_err_probe(&client->dev, PTR_ERR(data->battery),
"failed to register battery\n");
ret = device_property_read_u32(&client->dev, "shunt-resistor-micro-ohms",
&data->battery_info.sense_resistor);
if (ret)
return dev_err_probe(&client->dev, ret,
"failed to get shunt-resistor-micro-ohms\n");
data->battery_info.sense_resistor = data->battery_info.sense_resistor / 1000;
ret = power_supply_get_battery_info(data->battery, &info);
if (ret)
return dev_err_probe(&client->dev, ret,
"failed to get battery information\n");
data->battery_info.battery_capacity_mah = info->charge_full_design_uah / 1000;
data->battery_info.voltage_min_mv = info->voltage_min_design_uv / 1000;
data->battery_info.voltage_max_mv = info->voltage_max_design_uv / 1000;
ret = stc3117_init(data);
if (ret)
return dev_err_probe(&client->dev, ret,
"failed to initialize of stc3117\n");
ret = devm_delayed_work_autocancel(&client->dev, &data->update_work,
fuel_gauge_update_work);
if (ret)
return ret;
schedule_delayed_work(&data->update_work, 0);
return 0;
}
static const struct i2c_device_id stc3117_id[] = {
{ "stc3117", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, stc3117_id);
static const struct of_device_id stc3117_of_match[] = {
{ .compatible = "st,stc3117" },
{ }
};
MODULE_DEVICE_TABLE(of, stc3117_of_match);
static struct i2c_driver stc3117_i2c_driver = {
.driver = {
.name = "stc3117_i2c_driver",
.of_match_table = stc3117_of_match,
},
.probe = stc3117_probe,
.id_table = stc3117_id,
};
module_i2c_driver(stc3117_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>");
MODULE_AUTHOR("Bhavin Sharma <bhavin.sharma@siliconsignals.io>");
MODULE_DESCRIPTION("STC3117 Fuel Gauge Driver");

View File

@ -667,7 +667,7 @@ out:
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
int status;
@ -681,7 +681,7 @@ static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, cha
static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct power_supply *psy = dev_to_psy(dev);
struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
unsigned long value;
int status;

View File

@ -37,6 +37,7 @@ static int battery_charge_counter = -1000;
static int battery_current = -1600;
static enum power_supply_charge_behaviour battery_charge_behaviour =
POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
static bool battery_extension;
static bool module_initialized;
@ -238,6 +239,87 @@ static const struct power_supply_config test_power_configs[] = {
},
};
static int test_power_battery_extmanufacture_year = 1234;
static int test_power_battery_exttemp_max = 1000;
static const enum power_supply_property test_power_battery_extprops[] = {
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
POWER_SUPPLY_PROP_TEMP_MAX,
};
static int test_power_battery_extget_property(struct power_supply *psy,
const struct power_supply_ext *ext,
void *ext_data,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
val->intval = test_power_battery_extmanufacture_year;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
val->intval = test_power_battery_exttemp_max;
break;
default:
return -EINVAL;
}
return 0;
}
static int test_power_battery_extset_property(struct power_supply *psy,
const struct power_supply_ext *ext,
void *ext_data,
enum power_supply_property psp,
const union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
test_power_battery_extmanufacture_year = val->intval;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
test_power_battery_exttemp_max = val->intval;
break;
default:
return -EINVAL;
}
return 0;
}
static int test_power_battery_extproperty_is_writeable(struct power_supply *psy,
const struct power_supply_ext *ext,
void *ext_data,
enum power_supply_property psp)
{
return true;
}
static const struct power_supply_ext test_power_battery_ext = {
.name = "test_power",
.properties = test_power_battery_extprops,
.num_properties = ARRAY_SIZE(test_power_battery_extprops),
.get_property = test_power_battery_extget_property,
.set_property = test_power_battery_extset_property,
.property_is_writeable = test_power_battery_extproperty_is_writeable,
};
static void test_power_configure_battery_extension(bool enable)
{
struct power_supply *psy;
psy = test_power_supplies[TEST_BATTERY];
if (enable) {
if (power_supply_register_extension(psy, &test_power_battery_ext, &psy->dev,
NULL)) {
pr_err("registering battery extension failed\n");
return;
}
} else {
power_supply_unregister_extension(psy, &test_power_battery_ext);
}
battery_extension = enable;
}
static int __init test_power_init(void)
{
int i;
@ -258,6 +340,8 @@ static int __init test_power_init(void)
}
}
test_power_configure_battery_extension(true);
module_initialized = true;
return 0;
failed:
@ -524,6 +608,26 @@ static int param_set_battery_current(const char *key,
#define param_get_battery_current param_get_int
static int param_set_battery_extension(const char *key,
const struct kernel_param *kp)
{
bool prev_battery_extension;
int ret;
prev_battery_extension = battery_extension;
ret = param_set_bool(key, kp);
if (ret)
return ret;
if (prev_battery_extension != battery_extension)
test_power_configure_battery_extension(battery_extension);
return 0;
}
#define param_get_battery_extension param_get_bool
static const struct kernel_param_ops param_ops_ac_online = {
.set = param_set_ac_online,
.get = param_get_ac_online,
@ -574,6 +678,11 @@ static const struct kernel_param_ops param_ops_battery_current = {
.get = param_get_battery_current,
};
static const struct kernel_param_ops param_ops_battery_extension = {
.set = param_set_battery_extension,
.get = param_get_battery_extension,
};
#define param_check_ac_online(name, p) __param_check(name, p, void);
#define param_check_usb_online(name, p) __param_check(name, p, void);
#define param_check_battery_status(name, p) __param_check(name, p, void);
@ -584,6 +693,7 @@ static const struct kernel_param_ops param_ops_battery_current = {
#define param_check_battery_voltage(name, p) __param_check(name, p, void);
#define param_check_battery_charge_counter(name, p) __param_check(name, p, void);
#define param_check_battery_current(name, p) __param_check(name, p, void);
#define param_check_battery_extension(name, p) __param_check(name, p, void);
module_param(ac_online, ac_online, 0644);
@ -621,6 +731,9 @@ MODULE_PARM_DESC(battery_charge_counter,
module_param(battery_current, battery_current, 0644);
MODULE_PARM_DESC(battery_current, "battery current (milliampere)");
module_param(battery_extension, battery_extension, 0644);
MODULE_PARM_DESC(battery_extension, "battery extension");
MODULE_DESCRIPTION("Power supply driver for testing");
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -287,7 +287,6 @@ out:
static enum power_supply_property ug3105_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
@ -316,9 +315,6 @@ static int ug3105_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = chip->info->technology;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
break;

View File

@ -61,6 +61,7 @@ struct bq27xxx_device_info {
struct bq27xxx_access_methods bus;
struct bq27xxx_reg_cache cache;
int charge_design_full;
int voltage_min_design;
bool removed;
unsigned long last_update;
union power_supply_propval last_status;

View File

@ -15,6 +15,8 @@
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/rwsem.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
@ -40,7 +42,7 @@ enum {
};
/* What algorithm is the charger using? */
enum {
enum power_supply_charge_type {
POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0,
POWER_SUPPLY_CHARGE_TYPE_NONE,
POWER_SUPPLY_CHARGE_TYPE_TRICKLE, /* slow speed */
@ -99,6 +101,7 @@ enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CHARGE_TYPES,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
@ -245,6 +248,7 @@ struct power_supply_desc {
const char *name;
enum power_supply_type type;
u8 charge_behaviours;
u32 charge_types;
u32 usb_types;
const enum power_supply_property *properties;
size_t num_properties;
@ -281,6 +285,28 @@ struct power_supply_desc {
int use_for_apm;
};
struct power_supply_ext {
const char *const name;
u8 charge_behaviours;
const enum power_supply_property *properties;
size_t num_properties;
int (*get_property)(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
union power_supply_propval *val);
int (*set_property)(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp,
const union power_supply_propval *val);
int (*property_is_writeable)(struct power_supply *psy,
const struct power_supply_ext *ext,
void *data,
enum power_supply_property psp);
};
struct power_supply {
const struct power_supply_desc *desc;
@ -300,10 +326,13 @@ struct power_supply {
struct delayed_work deferred_register_work;
spinlock_t changed_lock;
bool changed;
bool update_groups;
bool initialized;
bool removing;
atomic_t use_cnt;
struct power_supply_battery_info *battery_info;
struct rw_semaphore extensions_sem; /* protects "extensions" */
struct list_head extensions;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd;
struct thermal_cooling_device *tcd;
@ -318,6 +347,8 @@ struct power_supply {
#endif
};
#define dev_to_psy(__dev) container_of_const(__dev, struct power_supply, dev)
/*
* This is recommended structure to specify static power supply parameters.
* Generic one, parametrizable for different power supplies. Power supply
@ -878,10 +909,18 @@ devm_power_supply_register(struct device *parent,
extern void power_supply_unregister(struct power_supply *psy);
extern int power_supply_powers(struct power_supply *psy, struct device *dev);
extern int __must_check
power_supply_register_extension(struct power_supply *psy,
const struct power_supply_ext *ext,
struct device *dev,
void *data);
extern void power_supply_unregister_extension(struct power_supply *psy,
const struct power_supply_ext *ext);
#define to_power_supply(device) container_of(device, struct power_supply, dev)
extern void *power_supply_get_drvdata(struct power_supply *psy);
extern int power_supply_for_each_device(void *data, int (*fn)(struct device *dev, void *data));
extern int power_supply_for_each_psy(void *data, int (*fn)(struct power_supply *psy, void *data));
static inline bool power_supply_is_amp_property(enum power_supply_property psp)
{
@ -944,6 +983,11 @@ ssize_t power_supply_charge_behaviour_show(struct device *dev,
char *buf);
int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf);
ssize_t power_supply_charge_types_show(struct device *dev,
unsigned int available_types,
enum power_supply_charge_type current_type,
char *buf);
int power_supply_charge_types_parse(unsigned int available_types, const char *buf);
#else
static inline
ssize_t power_supply_charge_behaviour_show(struct device *dev,
@ -959,6 +1003,20 @@ static inline int power_supply_charge_behaviour_parse(unsigned int available_beh
{
return -EOPNOTSUPP;
}
static inline
ssize_t power_supply_charge_types_show(struct device *dev,
unsigned int available_types,
enum power_supply_charge_type current_type,
char *buf)
{
return -EOPNOTSUPP;
}
static inline int power_supply_charge_types_parse(unsigned int available_types, const char *buf)
{
return -EOPNOTSUPP;
}
#endif
#endif /* __LINUX_POWER_SUPPLY_H__ */