mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 11:37:47 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git
This commit is contained in:
commit
3be3276b26
@ -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
|
||||
|
@ -22,6 +22,9 @@ properties:
|
||||
- enum:
|
||||
- atmel,sama5d2-shdwc
|
||||
- microchip,sam9x60-shdwc
|
||||
- items:
|
||||
- const: microchip,sam9x7-shdwc
|
||||
- const: microchip,sam9x60-shdwc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -21,6 +21,7 @@ properties:
|
||||
- ti,bq24192i
|
||||
- ti,bq24196
|
||||
- ti,bq24296
|
||||
- ti,bq24297
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 = {
|
||||
|
@ -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. */ }
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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, ®_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, ®_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, ®_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, ®_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, ®_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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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[] = {
|
||||
|
@ -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[] = {
|
||||
|
@ -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,
|
||||
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, <c4162l_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, <c4162l_desc,
|
||||
<c4162l_config);
|
||||
/* Duplicate the default descriptor to set name based on chip_info. */
|
||||
desc = devm_kmemdup(dev, <c4162l_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, <c4162l_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)<c4015_chip_info },
|
||||
{ "ltc4162-f", (kernel_ulong_t)<c4162f_chip_info },
|
||||
{ "ltc4162-l", (kernel_ulong_t)<c4162l_chip_info },
|
||||
{ "ltc4162-s", (kernel_ulong_t)<c4162s_chip_info },
|
||||
{ }
|
||||
};
|
||||
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 = <c4015_chip_info },
|
||||
{ .compatible = "lltc,ltc4162-f", .data = <c4162f_chip_info },
|
||||
{ .compatible = "lltc,ltc4162-l", .data = <c4162l_chip_info },
|
||||
{ .compatible = "lltc,ltc4162-s", .data = <c4162s_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc4162l_of_match);
|
||||
|
||||
|
@ -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, ®_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, ®_val);
|
||||
val->intval = max172xx_capacity_to_ps(reg_val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
ret = regmap_read(info->regmap, MAX172XX_DEV_NAME, ®_val);
|
||||
reg_val = FIELD_GET(MAX172XX_DEV_NAME_TYPE_MASK, reg_val);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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(®->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(®->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(®->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()).
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
612
drivers/power/supply/stc3117_fuel_gauge.c
Normal file
612
drivers/power/supply/stc3117_fuel_gauge.c
Normal 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");
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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__ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user