You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset changes from Sebastian Reichel: "New chip/feature support: - bq27xxx: support updating battery config from DT - bq24190: support loading battery charge info from DT - LTC2941: add LTC2942/LTC2944 support - max17042: add ACPI support - max1721x: new driver Misc: - Move bq27xxx w1 driver from w1 into power-supply subsystem - Introduce power_supply_set_input_current_limit_from_supplier - constify stuff - some minor fixes" * tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (39 commits) power: supply: bq27xxx: enable writing capacity values for bq27421 power: supply: bq24190_charger: Get input_current_limit from our supplier power: supply: bq24190_charger: Export 5V boost converter as regulator power: supply: bq24190_charger: Add power_supply_battery_info support power: supply: bq24190_charger: Add property system-minimum-microvolt power: supply: bq24190_charger: Enable devicetree config dt-bindings: power: supply: Add docs for TI BQ24190 battery charger power: supply: bq27xxx: Remove duplicate chip data arrays power: supply: bq27xxx: Enable data memory update for certain chips power: supply: bq27xxx: Add chip IDs for previously shadowed chips power: supply: bq27xxx: Create single chip data table power: supply: bq24190_charger: Add ti,bq24192i to devicetree table power: supply: bq24190_charger: Add input_current_limit property power: supply: Add power_supply_set_input_current_limit_from_supplier helper power: supply: max17042_battery: Fix compiler warning power: supply: core: Delete two error messages for a failed memory allocation in power_supply_check_supplies() power: supply: make device_attribute const power: supply: max17042_battery: Fix ACPI interrupt issues power: supply: max17042_battery: Add support for ACPI enumeration power: supply: lp8788: Make several arrays static const * const ...
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
TI BQ24190 Li-Ion Battery Charger
|
||||
|
||||
Required properties:
|
||||
- compatible: contains one of the following:
|
||||
* "ti,bq24190"
|
||||
* "ti,bq24192i"
|
||||
- reg: integer, I2C address of the charger.
|
||||
- interrupts[-extended]: configuration for charger INT pin.
|
||||
|
||||
Optional properties:
|
||||
- monitored-battery: phandle of battery characteristics devicetree node
|
||||
The charger uses the following battery properties:
|
||||
+ precharge-current-microamp: maximum charge current during precharge
|
||||
phase (typically 20% of battery capacity).
|
||||
+ charge-term-current-microamp: a charge cycle terminates when the
|
||||
battery voltage is above recharge threshold, and the current is below
|
||||
this setting (typically 10% of battery capacity).
|
||||
See also Documentation/devicetree/bindings/power/supply/battery.txt
|
||||
- ti,system-minimum-microvolt: when power is connected and the battery is below
|
||||
minimum system voltage, the system will be regulated above this setting.
|
||||
|
||||
Notes:
|
||||
- Some circuit boards wire the chip's "OTG" pin high (enabling 500mA default
|
||||
charge current on USB SDP ports, among other features). To simulate this on
|
||||
boards that wire the pin to a GPIO, set a gpio-hog.
|
||||
|
||||
Example:
|
||||
|
||||
bat: battery {
|
||||
compatible = "simple-battery";
|
||||
precharge-current-microamp = <256000>;
|
||||
charge-term-current-microamp = <128000>;
|
||||
// etc.
|
||||
};
|
||||
|
||||
bq24190: charger@6a {
|
||||
compatible = "ti,bq24190";
|
||||
reg = <0x6a>;
|
||||
interrupts-extended = <&gpiochip 10 IRQ_TYPE_EDGE_FALLING>;
|
||||
monitored-battery = <&bat>;
|
||||
ti,system-minimum-microvolt = <3200000>;
|
||||
};
|
||||
|
||||
&twl_gpio {
|
||||
otg {
|
||||
gpio-hog;
|
||||
gpios = <6 0>;
|
||||
output-high;
|
||||
line-name = "otg-gpio";
|
||||
};
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
binding for LTC2941 and LTC2943 battery gauges
|
||||
binding for LTC2941, LTC2942, LTC2943 and LTC2944 battery gauges
|
||||
|
||||
Both the LTC2941 and LTC2943 measure battery capacity.
|
||||
The LTC2943 is compatible with the LTC2941, it adds voltage and
|
||||
temperature monitoring, and uses a slightly different conversion
|
||||
formula for the charge counter.
|
||||
All chips measure battery capacity.
|
||||
The LTC2942 is pin compatible with the LTC2941, it adds voltage and
|
||||
temperature monitoring, and is runtime detected. LTC2943 and LTC2944
|
||||
is software compatible, uses a slightly different conversion formula
|
||||
for the charge counter and adds voltage, current and temperature monitoring.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "lltc,ltc2941" or "lltc,ltc2943" which also
|
||||
indicates the type of I2C chip attached.
|
||||
- compatible: Should contain "lltc,ltc2941", "lltc,ltc2942", "lltc,ltc2943"
|
||||
or "lltc,ltc2944" which also indicates the type of I2C chip attached.
|
||||
- reg: The 7-bit I2C address.
|
||||
- lltc,resistor-sense: The sense resistor value in milli-ohms. Can be a 32-bit
|
||||
negative value when the battery has been connected to the wrong end of the
|
||||
|
||||
@@ -171,8 +171,8 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev,
|
||||
|
||||
for_each_child_of_node(np, cnp) {
|
||||
if (of_property_read_u32(cnp, "reg", &wk_input)) {
|
||||
dev_warn(&pdev->dev, "reg property is missing for %s\n",
|
||||
cnp->full_name);
|
||||
dev_warn(&pdev->dev, "reg property is missing for %pOF\n",
|
||||
cnp);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,15 @@ config BATTERY_BQ27XXX_I2C
|
||||
Say Y here to enable support for batteries with BQ27xxx chips
|
||||
connected over an I2C bus.
|
||||
|
||||
config BATTERY_BQ27XXX_HDQ
|
||||
tristate "BQ27xxx HDQ support"
|
||||
depends on BATTERY_BQ27XXX
|
||||
depends on W1
|
||||
default y
|
||||
help
|
||||
Say Y here to enable support for batteries with BQ27xxx chips
|
||||
connected over an HDQ bus.
|
||||
|
||||
config BATTERY_BQ27XXX_DT_UPDATES_NVM
|
||||
bool "BQ27xxx support for update of NVM/flash data memory"
|
||||
depends on BATTERY_BQ27XXX_I2C
|
||||
@@ -313,6 +322,19 @@ config BATTERY_MAX17042
|
||||
with MAX17042. This driver also supports max17047/50 chips which are
|
||||
improved version of max17042.
|
||||
|
||||
config BATTERY_MAX1721X
|
||||
tristate "MAX17211/MAX17215 standalone gas-gauge"
|
||||
depends on W1
|
||||
select REGMAP_W1
|
||||
help
|
||||
MAX1721x is fuel-gauge systems for lithium-ion (Li+) batteries
|
||||
in handheld and portable equipment. MAX17211 used with single cell
|
||||
battery. MAX17215 designed for muticell battery. Both them have
|
||||
OneWire (W1) host interface.
|
||||
|
||||
Say Y here to enable support for the MAX17211/MAX17215 standalone
|
||||
battery gas-gauge.
|
||||
|
||||
config BATTERY_Z2
|
||||
tristate "Z2 battery driver"
|
||||
depends on I2C && MACH_ZIPIT2
|
||||
@@ -365,6 +387,7 @@ config BATTERY_RX51
|
||||
config CHARGER_CPCAP
|
||||
tristate "CPCAP PMIC Charger Driver"
|
||||
depends on MFD_CPCAP && IIO
|
||||
depends on OMAP_USB2 || (!OMAP_USB2 && COMPILE_TEST)
|
||||
default MFD_CPCAP
|
||||
help
|
||||
Say Y to enable support for CPCAP PMIC charger driver for Motorola
|
||||
|
||||
@@ -38,12 +38,14 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
|
||||
obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o
|
||||
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
|
||||
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
|
||||
obj-$(CONFIG_BATTERY_BQ27XXX_HDQ) += bq27xxx_battery_hdq.o
|
||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
||||
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
|
||||
obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
|
||||
obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o
|
||||
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o
|
||||
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
|
||||
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
|
||||
obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o
|
||||
|
||||
@@ -596,9 +596,9 @@ static int act8945a_charger_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
irq = of_irq_get(pdev->dev.of_node, 0);
|
||||
if (irq == -EPROBE_DEFER) {
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed to find IRQ number\n");
|
||||
return -EPROBE_DEFER;
|
||||
return irq ?: -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* BQ27xxx battery monitor HDQ/1-wire driver
|
||||
*
|
||||
* Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power/bq27xxx_battery.h>
|
||||
|
||||
#include <linux/w1.h>
|
||||
|
||||
#define W1_FAMILY_BQ27000 0x01
|
||||
|
||||
#define HDQ_CMD_READ (0 << 7)
|
||||
#define HDQ_CMD_WRITE (1 << 7)
|
||||
|
||||
static int F_ID;
|
||||
module_param(F_ID, int, S_IRUSR);
|
||||
MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device");
|
||||
|
||||
static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&sl->master->bus_mutex);
|
||||
w1_write_8(sl->master, HDQ_CMD_READ | reg);
|
||||
val = w1_read_8(sl->master);
|
||||
mutex_unlock(&sl->master->bus_mutex);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg,
|
||||
bool single)
|
||||
{
|
||||
struct w1_slave *sl = dev_to_w1_slave(di->dev);
|
||||
unsigned int timeout = 3;
|
||||
int upper, lower;
|
||||
int temp;
|
||||
|
||||
if (!single) {
|
||||
/*
|
||||
* Make sure the value has not changed in between reading the
|
||||
* lower and the upper part
|
||||
*/
|
||||
upper = w1_bq27000_read(sl, reg + 1);
|
||||
do {
|
||||
temp = upper;
|
||||
if (upper < 0)
|
||||
return upper;
|
||||
|
||||
lower = w1_bq27000_read(sl, reg);
|
||||
if (lower < 0)
|
||||
return lower;
|
||||
|
||||
upper = w1_bq27000_read(sl, reg + 1);
|
||||
} while (temp != upper && --timeout);
|
||||
|
||||
if (timeout == 0)
|
||||
return -EIO;
|
||||
|
||||
return (upper << 8) | lower;
|
||||
}
|
||||
|
||||
return w1_bq27000_read(sl, reg);
|
||||
}
|
||||
|
||||
static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl)
|
||||
{
|
||||
struct bq27xxx_device_info *di;
|
||||
|
||||
di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&sl->dev, di);
|
||||
|
||||
di->dev = &sl->dev;
|
||||
di->chip = BQ27000;
|
||||
di->name = "bq27000-battery";
|
||||
di->bus.read = bq27xxx_battery_hdq_read;
|
||||
|
||||
return bq27xxx_battery_setup(di);
|
||||
}
|
||||
|
||||
static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl)
|
||||
{
|
||||
struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev);
|
||||
|
||||
bq27xxx_battery_teardown(di);
|
||||
}
|
||||
|
||||
static struct w1_family_ops bq27xxx_battery_hdq_fops = {
|
||||
.add_slave = bq27xxx_battery_hdq_add_slave,
|
||||
.remove_slave = bq27xxx_battery_hdq_remove_slave,
|
||||
};
|
||||
|
||||
static struct w1_family bq27xxx_battery_hdq_family = {
|
||||
.fid = W1_FAMILY_BQ27000,
|
||||
.fops = &bq27xxx_battery_hdq_fops,
|
||||
};
|
||||
|
||||
static int __init bq27xxx_battery_hdq_init(void)
|
||||
{
|
||||
if (F_ID)
|
||||
bq27xxx_battery_hdq_family.fid = F_ID;
|
||||
|
||||
return w1_register_family(&bq27xxx_battery_hdq_family);
|
||||
}
|
||||
module_init(bq27xxx_battery_hdq_init);
|
||||
|
||||
static void __exit bq27xxx_battery_hdq_exit(void)
|
||||
{
|
||||
w1_unregister_family(&bq27xxx_battery_hdq_family);
|
||||
}
|
||||
module_exit(bq27xxx_battery_hdq_exit);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments Ltd");
|
||||
MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000));
|
||||
@@ -230,7 +230,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
||||
{ "bq27210", BQ27010 },
|
||||
{ "bq27500", BQ2750X },
|
||||
{ "bq27510", BQ2751X },
|
||||
{ "bq27520", BQ2751X },
|
||||
{ "bq27520", BQ2752X },
|
||||
{ "bq27500-1", BQ27500 },
|
||||
{ "bq27510g1", BQ27510G1 },
|
||||
{ "bq27510g2", BQ27510G2 },
|
||||
@@ -240,16 +240,16 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
||||
{ "bq27520g3", BQ27520G3 },
|
||||
{ "bq27520g4", BQ27520G4 },
|
||||
{ "bq27530", BQ27530 },
|
||||
{ "bq27531", BQ27530 },
|
||||
{ "bq27531", BQ27531 },
|
||||
{ "bq27541", BQ27541 },
|
||||
{ "bq27542", BQ27541 },
|
||||
{ "bq27546", BQ27541 },
|
||||
{ "bq27742", BQ27541 },
|
||||
{ "bq27542", BQ27542 },
|
||||
{ "bq27546", BQ27546 },
|
||||
{ "bq27742", BQ27742 },
|
||||
{ "bq27545", BQ27545 },
|
||||
{ "bq27421", BQ27421 },
|
||||
{ "bq27425", BQ27421 },
|
||||
{ "bq27441", BQ27421 },
|
||||
{ "bq27621", BQ27421 },
|
||||
{ "bq27425", BQ27425 },
|
||||
{ "bq27441", BQ27441 },
|
||||
{ "bq27621", BQ27621 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
|
||||
|
||||
@@ -1632,8 +1632,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cm = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct charger_manager), GFP_KERNEL);
|
||||
cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
|
||||
if (!cm)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1645,12 +1644,14 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
/* Initialize alarm timer */
|
||||
if (alarmtimer_get_rtcdev()) {
|
||||
cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
|
||||
if (!cm_timer)
|
||||
return -ENOMEM;
|
||||
alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following two do not need to be errors.
|
||||
* Users may intentionally ignore those two features.
|
||||
* Some of the following do not need to be errors.
|
||||
* Users may intentionally ignore those features.
|
||||
*/
|
||||
if (desc->fullbatt_uV == 0) {
|
||||
dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
|
||||
|
||||
@@ -663,7 +663,7 @@ 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,
|
||||
@@ -708,7 +708,7 @@ 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,
|
||||
|
||||
@@ -665,7 +665,7 @@ 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,
|
||||
@@ -711,7 +711,7 @@ 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,
|
||||
|
||||
@@ -626,7 +626,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev,
|
||||
{
|
||||
struct lp8788_charger *pchg = dev_get_drvdata(dev);
|
||||
enum lp8788_charging_state state;
|
||||
char *desc[LP8788_MAX_CHG_STATE] = {
|
||||
static const char * const desc[LP8788_MAX_CHG_STATE] = {
|
||||
[LP8788_OFF] = "CHARGER OFF",
|
||||
[LP8788_WARM_UP] = "WARM UP",
|
||||
[LP8788_LOW_INPUT] = "LOW INPUT STATE",
|
||||
@@ -650,8 +650,10 @@ static ssize_t lp8788_show_eoc_time(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lp8788_charger *pchg = dev_get_drvdata(dev);
|
||||
char *stime[] = { "400ms", "5min", "10min", "15min",
|
||||
"20min", "25min", "30min", "No timeout" };
|
||||
static const char * const stime[] = {
|
||||
"400ms", "5min", "10min", "15min",
|
||||
"20min", "25min", "30min", "No timeout"
|
||||
};
|
||||
u8 val;
|
||||
|
||||
lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
|
||||
@@ -665,9 +667,13 @@ static ssize_t lp8788_show_eoc_level(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lp8788_charger *pchg = dev_get_drvdata(dev);
|
||||
char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" };
|
||||
char *relative_level[] = { "5%", "10%", "15%", "20%" };
|
||||
char *level;
|
||||
static const char * const abs_level[] = {
|
||||
"25mA", "49mA", "75mA", "98mA"
|
||||
};
|
||||
static const char * const relative_level[] = {
|
||||
"5%", "10%", "15%", "20%"
|
||||
};
|
||||
const char *level;
|
||||
u8 val;
|
||||
u8 mode;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* I2C client/driver for the Linear Technology LTC2941 and LTC2943
|
||||
* Battery Gas Gauge IC
|
||||
* I2C client/driver for the Linear Technology LTC2941, LTC2942, LTC2943
|
||||
* and LTC2944 Battery Gas Gauge IC
|
||||
*
|
||||
* Copyright (C) 2014 Topic Embedded Systems
|
||||
*
|
||||
@@ -34,35 +34,39 @@ enum ltc294x_reg {
|
||||
LTC294X_REG_CONTROL = 0x01,
|
||||
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
|
||||
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
|
||||
LTC294X_REG_THRESH_HIGH_MSB = 0x04,
|
||||
LTC294X_REG_THRESH_HIGH_LSB = 0x05,
|
||||
LTC294X_REG_THRESH_LOW_MSB = 0x06,
|
||||
LTC294X_REG_THRESH_LOW_LSB = 0x07,
|
||||
LTC294X_REG_VOLTAGE_MSB = 0x08,
|
||||
LTC294X_REG_VOLTAGE_LSB = 0x09,
|
||||
LTC294X_REG_CURRENT_MSB = 0x0E,
|
||||
LTC294X_REG_CURRENT_LSB = 0x0F,
|
||||
LTC294X_REG_TEMPERATURE_MSB = 0x14,
|
||||
LTC294X_REG_TEMPERATURE_LSB = 0x15,
|
||||
LTC294X_REG_VOLTAGE_MSB = 0x08,
|
||||
LTC294X_REG_VOLTAGE_LSB = 0x09,
|
||||
LTC2942_REG_TEMPERATURE_MSB = 0x0C,
|
||||
LTC2942_REG_TEMPERATURE_LSB = 0x0D,
|
||||
LTC2943_REG_CURRENT_MSB = 0x0E,
|
||||
LTC2943_REG_CURRENT_LSB = 0x0F,
|
||||
LTC2943_REG_TEMPERATURE_MSB = 0x14,
|
||||
LTC2943_REG_TEMPERATURE_LSB = 0x15,
|
||||
};
|
||||
|
||||
#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
|
||||
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
|
||||
enum ltc294x_id {
|
||||
LTC2941_ID,
|
||||
LTC2942_ID,
|
||||
LTC2943_ID,
|
||||
LTC2944_ID,
|
||||
};
|
||||
|
||||
#define LTC2941_REG_STATUS_CHIP_ID BIT(7)
|
||||
|
||||
#define LTC2942_REG_CONTROL_MODE_SCAN (BIT(7) | BIT(6))
|
||||
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
|
||||
#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3))
|
||||
#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0))
|
||||
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
|
||||
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
|
||||
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
|
||||
|
||||
#define LTC2941_NUM_REGS 0x08
|
||||
#define LTC2943_NUM_REGS 0x18
|
||||
|
||||
struct ltc294x_info {
|
||||
struct i2c_client *client; /* I2C Client pointer */
|
||||
struct power_supply *supply; /* Supply pointer */
|
||||
struct power_supply_desc supply_desc; /* Supply description */
|
||||
struct delayed_work work; /* Work scheduler */
|
||||
unsigned long num_regs; /* Number of registers (chip type) */
|
||||
enum ltc294x_id id; /* Chip type */
|
||||
int charge; /* Last charge register content */
|
||||
int r_sense; /* mOhm */
|
||||
int Qlsb; /* nAh */
|
||||
@@ -145,9 +149,18 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
|
||||
|
||||
control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
|
||||
LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
|
||||
/* Put the 2943 into "monitor" mode, so it measures every 10 sec */
|
||||
if (info->num_regs == LTC2943_NUM_REGS)
|
||||
/* Put device into "monitor" mode */
|
||||
switch (info->id) {
|
||||
case LTC2942_ID: /* 2942 measures every 2 sec */
|
||||
control |= LTC2942_REG_CONTROL_MODE_SCAN;
|
||||
break;
|
||||
case LTC2943_ID:
|
||||
case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */
|
||||
control |= LTC2943_REG_CONTROL_MODE_SCAN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (value != control) {
|
||||
ret = ltc294x_write_regs(info->client,
|
||||
@@ -252,7 +265,24 @@ static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
*val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
|
||||
switch (info->id) {
|
||||
case LTC2943_ID:
|
||||
value *= 23600 * 2;
|
||||
value /= 0xFFFF;
|
||||
value *= 1000 / 2;
|
||||
break;
|
||||
case LTC2944_ID:
|
||||
value *= 70800 / 5*4;
|
||||
value /= 0xFFFF;
|
||||
value *= 1000 * 5/4;
|
||||
break;
|
||||
default:
|
||||
value *= 6000 * 10;
|
||||
value /= 0xFFFF;
|
||||
value *= 1000 / 10;
|
||||
break;
|
||||
}
|
||||
*val = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -263,27 +293,38 @@ static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
|
||||
s32 value;
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_CURRENT_MSB, &datar[0], 2);
|
||||
LTC2943_REG_CURRENT_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
value -= 0x7FFF;
|
||||
if (info->id == LTC2944_ID)
|
||||
value *= 64000;
|
||||
else
|
||||
value *= 60000;
|
||||
/* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
|
||||
* the formula below keeps everything in s32 range while preserving
|
||||
* enough digits */
|
||||
*val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
|
||||
*val = 1000 * (value / (info->r_sense * 0x7FFF)); /* in uA */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
enum ltc294x_reg reg;
|
||||
int ret;
|
||||
u8 datar[2];
|
||||
u32 value;
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
/* Full-scale is 510 Kelvin, convert to centidegrees */
|
||||
*val = (((51000 * value) / 0xFFFF) - 27215);
|
||||
if (info->id == LTC2942_ID) {
|
||||
reg = LTC2942_REG_TEMPERATURE_MSB;
|
||||
value = 60000; /* Full-scale is 600 Kelvin */
|
||||
} else {
|
||||
reg = LTC2943_REG_TEMPERATURE_MSB;
|
||||
value = 51000; /* Full-scale is 510 Kelvin */
|
||||
}
|
||||
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
|
||||
value *= (datar[0] << 8) | datar[1];
|
||||
/* Convert to centidegrees */
|
||||
*val = value / 0xFFFF - 27215;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -357,8 +398,8 @@ static enum power_supply_property ltc294x_properties[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
};
|
||||
|
||||
static int ltc294x_i2c_remove(struct i2c_client *client)
|
||||
@@ -375,10 +416,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
{
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct ltc294x_info *info;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
u32 prescaler_exp;
|
||||
s32 r_sense;
|
||||
struct device_node *np;
|
||||
u8 status;
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL)
|
||||
@@ -388,7 +430,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
|
||||
np = of_node_get(client->dev.of_node);
|
||||
|
||||
info->num_regs = (unsigned long)of_device_get_match_data(&client->dev);
|
||||
info->id = (enum ltc294x_id)of_device_get_match_data(&client->dev);
|
||||
info->supply_desc.name = np->name;
|
||||
|
||||
/* r_sense can be negative, when sense+ is connected to the battery
|
||||
@@ -409,7 +451,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
|
||||
}
|
||||
|
||||
if (info->num_regs == LTC2943_NUM_REGS) {
|
||||
if (info->id == LTC2943_ID) {
|
||||
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
|
||||
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
|
||||
info->Qlsb = ((340 * 50000) / r_sense) /
|
||||
@@ -421,21 +463,39 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
(128 / (1 << prescaler_exp));
|
||||
}
|
||||
|
||||
/* Read status register to check for LTC2942 */
|
||||
if (info->id == LTC2941_ID || info->id == LTC2942_ID) {
|
||||
ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Could not read status register\n");
|
||||
return ret;
|
||||
}
|
||||
if (status & LTC2941_REG_STATUS_CHIP_ID)
|
||||
info->id = LTC2941_ID;
|
||||
else
|
||||
info->id = LTC2942_ID;
|
||||
}
|
||||
|
||||
info->client = client;
|
||||
info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
info->supply_desc.properties = ltc294x_properties;
|
||||
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
|
||||
switch (info->id) {
|
||||
case LTC2944_ID:
|
||||
case LTC2943_ID:
|
||||
info->supply_desc.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties);
|
||||
else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
|
||||
break;
|
||||
case LTC2942_ID:
|
||||
info->supply_desc.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 1;
|
||||
else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
|
||||
info->supply_desc.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 2;
|
||||
else
|
||||
break;
|
||||
case LTC2941_ID:
|
||||
default:
|
||||
info->supply_desc.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 3;
|
||||
break;
|
||||
}
|
||||
info->supply_desc.get_property = ltc294x_get_property;
|
||||
info->supply_desc.set_property = ltc294x_set_property;
|
||||
info->supply_desc.property_is_writeable = ltc294x_property_is_writeable;
|
||||
@@ -492,8 +552,10 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
|
||||
|
||||
|
||||
static const struct i2c_device_id ltc294x_i2c_id[] = {
|
||||
{"ltc2941", LTC2941_NUM_REGS},
|
||||
{"ltc2943", LTC2943_NUM_REGS},
|
||||
{ "ltc2941", LTC2941_ID, },
|
||||
{ "ltc2942", LTC2942_ID, },
|
||||
{ "ltc2943", LTC2943_ID, },
|
||||
{ "ltc2944", LTC2944_ID, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
|
||||
@@ -501,11 +563,19 @@ MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
|
||||
static const struct of_device_id ltc294x_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "lltc,ltc2941",
|
||||
.data = (void *)LTC2941_NUM_REGS
|
||||
.data = (void *)LTC2941_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "lltc,ltc2942",
|
||||
.data = (void *)LTC2942_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "lltc,ltc2943",
|
||||
.data = (void *)LTC2943_NUM_REGS
|
||||
.data = (void *)LTC2943_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "lltc,ltc2944",
|
||||
.data = (void *)LTC2944_ID,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
@@ -525,5 +595,5 @@ module_i2c_driver(ltc294x_driver);
|
||||
|
||||
MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
|
||||
MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
|
||||
MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
|
||||
MODULE_DESCRIPTION("LTC2941/LTC2942/LTC2943/LTC2944 Battery Gas Gauge IC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* This driver is based on max17040_battery.c
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -982,6 +983,8 @@ static int max17042_probe(struct i2c_client *client,
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
const struct acpi_device_id *acpi_id = NULL;
|
||||
struct device *dev = &client->dev;
|
||||
struct max17042_chip *chip;
|
||||
int ret;
|
||||
int i;
|
||||
@@ -995,7 +998,15 @@ static int max17042_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
chip->chip_type = id->driver_data;
|
||||
if (id) {
|
||||
chip->chip_type = id->driver_data;
|
||||
} else {
|
||||
acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!acpi_id)
|
||||
return -ENODEV;
|
||||
|
||||
chip->chip_type = acpi_id->driver_data;
|
||||
}
|
||||
chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize regmap\n");
|
||||
@@ -1039,11 +1050,18 @@ static int max17042_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||
|
||||
/*
|
||||
* On ACPI systems the IRQ may be handled by ACPI-event code,
|
||||
* so we need to share (if the ACPI code is willing to share).
|
||||
*/
|
||||
if (acpi_id)
|
||||
flags |= IRQF_SHARED | IRQF_PROBE_SHARED;
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL,
|
||||
max17042_thread_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
max17042_thread_handler, flags,
|
||||
chip->battery->desc->name,
|
||||
chip);
|
||||
if (!ret) {
|
||||
@@ -1053,10 +1071,13 @@ static int max17042_probe(struct i2c_client *client,
|
||||
max17042_set_soc_threshold(chip, 1);
|
||||
} else {
|
||||
client->irq = 0;
|
||||
dev_err(&client->dev, "%s(): cannot get IRQ\n",
|
||||
__func__);
|
||||
if (ret != -EBUSY)
|
||||
dev_err(&client->dev, "Failed to get IRQ\n");
|
||||
}
|
||||
}
|
||||
/* Not able to update the charge threshold when exceeded? -> disable */
|
||||
if (!client->irq)
|
||||
regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00);
|
||||
|
||||
regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if (val & STATUS_POR_BIT) {
|
||||
@@ -1104,6 +1125,14 @@ static int max17042_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
|
||||
max17042_resume);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id max17042_acpi_match[] = {
|
||||
{ "MAX17047", MAXIM_DEVICE_TYPE_MAX17047 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max17042_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max17042_dt_match[] = {
|
||||
{ .compatible = "maxim,max17042" },
|
||||
@@ -1125,6 +1154,7 @@ MODULE_DEVICE_TABLE(i2c, max17042_id);
|
||||
static struct i2c_driver max17042_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max17042",
|
||||
.acpi_match_table = ACPI_PTR(max17042_acpi_match),
|
||||
.of_match_table = of_match_ptr(max17042_dt_match),
|
||||
.pm = &max17042_pm_ops,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* 1-Wire implementation for Maxim Semiconductor
|
||||
* MAX7211/MAX17215 stanalone fuel gauge chip
|
||||
*
|
||||
* Copyright (C) 2017 Radioavionica Corporation
|
||||
* Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
|
||||
*
|
||||
* Use consistent with the GNU GPL is permitted,
|
||||
* provided that this copyright notice is
|
||||
* preserved in its entirety in all copies and derived works.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/w1.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#define W1_MAX1721X_FAMILY_ID 0x26
|
||||
#define DEF_DEV_NAME_MAX17211 "MAX17211"
|
||||
#define DEF_DEV_NAME_MAX17215 "MAX17215"
|
||||
#define DEF_DEV_NAME_UNKNOWN "UNKNOWN"
|
||||
#define DEF_MFG_NAME "MAXIM"
|
||||
|
||||
#define PSY_MAX_NAME_LEN 32
|
||||
|
||||
/* Number of valid register addresses in W1 mode */
|
||||
#define MAX1721X_MAX_REG_NR 0x1EF
|
||||
|
||||
/* Factory settings (nonvilatile registers) (W1 specific) */
|
||||
#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */
|
||||
/* Strings */
|
||||
#define MAX1721X_REG_MFG_STR 0x1CC
|
||||
#define MAX1721X_REG_MFG_NUMB 3
|
||||
#define MAX1721X_REG_DEV_STR 0x1DB
|
||||
#define MAX1721X_REG_DEV_NUMB 5
|
||||
/* HEX Strings */
|
||||
#define MAX1721X_REG_SER_HEX 0x1D8
|
||||
|
||||
/* MAX172XX Output Registers for W1 chips */
|
||||
#define MAX172XX_REG_STATUS 0x000 /* status reg */
|
||||
#define MAX172XX_BAT_PRESENT (1<<4) /* battery connected bit */
|
||||
#define MAX172XX_REG_DEVNAME 0x021 /* chip config */
|
||||
#define MAX172XX_DEV_MASK 0x000F /* chip type mask */
|
||||
#define MAX172X1_DEV 0x0001
|
||||
#define MAX172X5_DEV 0x0005
|
||||
#define MAX172XX_REG_TEMP 0x008 /* Temperature */
|
||||
#define MAX172XX_REG_BATT 0x0DA /* Battery voltage */
|
||||
#define MAX172XX_REG_CURRENT 0x00A /* Actual current */
|
||||
#define MAX172XX_REG_AVGCURRENT 0x00B /* Average current */
|
||||
#define MAX172XX_REG_REPSOC 0x006 /* Percentage of charge */
|
||||
#define MAX172XX_REG_DESIGNCAP 0x018 /* Design capacity */
|
||||
#define MAX172XX_REG_REPCAP 0x005 /* Average capacity */
|
||||
#define MAX172XX_REG_TTE 0x011 /* Time to empty */
|
||||
#define MAX172XX_REG_TTF 0x020 /* Time to full */
|
||||
|
||||
struct max17211_device_info {
|
||||
char name[PSY_MAX_NAME_LEN];
|
||||
struct power_supply *bat;
|
||||
struct power_supply_desc bat_desc;
|
||||
struct device *w1_dev;
|
||||
struct regmap *regmap;
|
||||
/* battery design format */
|
||||
unsigned int rsense; /* in tenths uOhm */
|
||||
char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1];
|
||||
char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1];
|
||||
char SerialNumber[13]; /* see get_sn_str() later for comment */
|
||||
};
|
||||
|
||||
/* Convert regs value to power_supply units */
|
||||
|
||||
static inline int max172xx_time_to_ps(unsigned int reg)
|
||||
{
|
||||
return reg * 5625 / 1000; /* in sec. */
|
||||
}
|
||||
|
||||
static inline int max172xx_percent_to_ps(unsigned int reg)
|
||||
{
|
||||
return reg / 256; /* in percent from 0 to 100 */
|
||||
}
|
||||
|
||||
static inline int max172xx_voltage_to_ps(unsigned int reg)
|
||||
{
|
||||
return reg * 1250; /* in uV */
|
||||
}
|
||||
|
||||
static inline int max172xx_capacity_to_ps(unsigned int reg)
|
||||
{
|
||||
return reg * 500; /* in uAh */
|
||||
}
|
||||
|
||||
/*
|
||||
* Current and temperature is signed values, so unsigned regs
|
||||
* value must be converted to signed type
|
||||
*/
|
||||
|
||||
static inline int max172xx_temperature_to_ps(unsigned int reg)
|
||||
{
|
||||
int val = (int16_t)(reg);
|
||||
|
||||
return val * 10 / 256; /* in tenths of deg. C */
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculating current registers resolution:
|
||||
*
|
||||
* RSense stored in 10^-5 Ohm, so mesaurment voltage must be
|
||||
* in 10^-11 Volts for get current in uA.
|
||||
* 16 bit current reg fullscale +/-51.2mV is 102400 uV.
|
||||
* So: 102400 / 65535 * 10^5 = 156252
|
||||
*/
|
||||
static inline int max172xx_current_to_voltage(unsigned int reg)
|
||||
{
|
||||
int val = (int16_t)(reg);
|
||||
|
||||
return val * 156252;
|
||||
}
|
||||
|
||||
|
||||
static inline struct max17211_device_info *
|
||||
to_device_info(struct power_supply *psy)
|
||||
{
|
||||
return power_supply_get_drvdata(psy);
|
||||
}
|
||||
|
||||
static int max1721x_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max17211_device_info *info = to_device_info(psy);
|
||||
unsigned int reg = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
/*
|
||||
* POWER_SUPPLY_PROP_PRESENT will always readable via
|
||||
* sysfs interface. Value return 0 if battery not
|
||||
* present or unaccesable via W1.
|
||||
*/
|
||||
val->intval =
|
||||
regmap_read(info->regmap, MAX172XX_REG_STATUS,
|
||||
®) ? 0 : !(reg & MAX172XX_BAT_PRESENT);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, ®);
|
||||
val->intval = max172xx_percent_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_BATT, ®);
|
||||
val->intval = max172xx_voltage_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, ®);
|
||||
val->intval = max172xx_capacity_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_AVG:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, ®);
|
||||
val->intval = max172xx_capacity_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_TTE, ®);
|
||||
val->intval = max172xx_time_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_TTF, ®);
|
||||
val->intval = max172xx_time_to_ps(reg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, ®);
|
||||
val->intval = max172xx_temperature_to_ps(reg);
|
||||
break;
|
||||
/* We need signed current, so must cast info->rsense to signed type */
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, ®);
|
||||
val->intval =
|
||||
max172xx_current_to_voltage(reg) / (int)info->rsense;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, ®);
|
||||
val->intval =
|
||||
max172xx_current_to_voltage(reg) / (int)info->rsense;
|
||||
break;
|
||||
/*
|
||||
* Strings already received and inited by probe.
|
||||
* We do dummy read for check battery still available.
|
||||
*/
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, ®);
|
||||
val->strval = info->DeviceName;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, ®);
|
||||
val->strval = info->ManufacturerName;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||
ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, ®);
|
||||
val->strval = info->SerialNumber;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property max1721x_battery_props[] = {
|
||||
/* int */
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
/* strings */
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
};
|
||||
|
||||
static int get_string(struct max17211_device_info *info,
|
||||
uint16_t reg, uint8_t nr, char *str)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (!str || !(reg == MAX1721X_REG_MFG_STR ||
|
||||
reg == MAX1721X_REG_DEV_STR))
|
||||
return -EFAULT;
|
||||
|
||||
while (nr--) {
|
||||
if (regmap_read(info->regmap, reg++, &val))
|
||||
return -EFAULT;
|
||||
*str++ = val>>8 & 0x00FF;
|
||||
*str++ = val & 0x00FF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Maxim say: Serial number is a hex string up to 12 hex characters */
|
||||
static int get_sn_string(struct max17211_device_info *info, char *str)
|
||||
{
|
||||
unsigned int val[3];
|
||||
|
||||
if (!str)
|
||||
return -EFAULT;
|
||||
|
||||
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0]))
|
||||
return -EFAULT;
|
||||
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1]))
|
||||
return -EFAULT;
|
||||
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2]))
|
||||
return -EFAULT;
|
||||
|
||||
snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX1721x registers description for w1-regmap
|
||||
*/
|
||||
static const struct regmap_range max1721x_allow_range[] = {
|
||||
regmap_reg_range(0, 0xDF), /* volatile data */
|
||||
regmap_reg_range(0x180, 0x1DF), /* non-volatile memory */
|
||||
regmap_reg_range(0x1E0, 0x1EF), /* non-volatile history (unused) */
|
||||
};
|
||||
|
||||
static const struct regmap_range max1721x_deny_range[] = {
|
||||
/* volatile data unused registers */
|
||||
regmap_reg_range(0x24, 0x26),
|
||||
regmap_reg_range(0x30, 0x31),
|
||||
regmap_reg_range(0x33, 0x34),
|
||||
regmap_reg_range(0x37, 0x37),
|
||||
regmap_reg_range(0x3B, 0x3C),
|
||||
regmap_reg_range(0x40, 0x41),
|
||||
regmap_reg_range(0x43, 0x44),
|
||||
regmap_reg_range(0x47, 0x49),
|
||||
regmap_reg_range(0x4B, 0x4C),
|
||||
regmap_reg_range(0x4E, 0xAF),
|
||||
regmap_reg_range(0xB1, 0xB3),
|
||||
regmap_reg_range(0xB5, 0xB7),
|
||||
regmap_reg_range(0xBF, 0xD0),
|
||||
regmap_reg_range(0xDB, 0xDB),
|
||||
/* hole between volatile and non-volatile registers */
|
||||
regmap_reg_range(0xE0, 0x17F),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table max1721x_regs = {
|
||||
.yes_ranges = max1721x_allow_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(max1721x_allow_range),
|
||||
.no_ranges = max1721x_deny_range,
|
||||
.n_no_ranges = ARRAY_SIZE(max1721x_deny_range),
|
||||
};
|
||||
|
||||
/*
|
||||
* Model Gauge M5 Algorithm output register
|
||||
* Volatile data (must not be cached)
|
||||
*/
|
||||
static const struct regmap_range max1721x_volatile_allow[] = {
|
||||
regmap_reg_range(0, 0xDF),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table max1721x_volatile_regs = {
|
||||
.yes_ranges = max1721x_volatile_allow,
|
||||
.n_yes_ranges = ARRAY_SIZE(max1721x_volatile_allow),
|
||||
};
|
||||
|
||||
/*
|
||||
* W1-regmap config
|
||||
*/
|
||||
static const struct regmap_config max1721x_regmap_w1_config = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.rd_table = &max1721x_regs,
|
||||
.volatile_table = &max1721x_volatile_regs,
|
||||
.max_register = MAX1721X_MAX_REG_NR,
|
||||
};
|
||||
|
||||
static int devm_w1_max1721x_add_device(struct w1_slave *sl)
|
||||
{
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max17211_device_info *info;
|
||||
|
||||
info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
sl->family_data = (void *)info;
|
||||
info->w1_dev = &sl->dev;
|
||||
|
||||
/*
|
||||
* power_supply class battery name translated from W1 slave device
|
||||
* unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0"
|
||||
* so, 26 (device family) correcpondent to max1721x devices.
|
||||
* Device name still unical for any numbers connected devices.
|
||||
*/
|
||||
snprintf(info->name, sizeof(info->name),
|
||||
"max1721x-%012X", (unsigned int)sl->reg_num.id);
|
||||
info->bat_desc.name = info->name;
|
||||
|
||||
/*
|
||||
* FixMe: battery device name exceed max len for thermal_zone device
|
||||
* name and translation to thermal_zone must be disabled.
|
||||
*/
|
||||
info->bat_desc.no_thermal = true;
|
||||
info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
info->bat_desc.properties = max1721x_battery_props;
|
||||
info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props);
|
||||
info->bat_desc.get_property = max1721x_battery_get_property;
|
||||
psy_cfg.drv_data = info;
|
||||
|
||||
/* regmap init */
|
||||
info->regmap = devm_regmap_init_w1(info->w1_dev,
|
||||
&max1721x_regmap_w1_config);
|
||||
if (IS_ERR(info->regmap)) {
|
||||
int err = PTR_ERR(info->regmap);
|
||||
|
||||
dev_err(info->w1_dev, "Failed to allocate register map: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* rsense init */
|
||||
info->rsense = 0;
|
||||
if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) {
|
||||
dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!info->rsense) {
|
||||
dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n");
|
||||
info->rsense = 1000; /* in regs in 10^-5 */
|
||||
}
|
||||
dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100);
|
||||
|
||||
if (get_string(info, MAX1721X_REG_MFG_STR,
|
||||
MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) {
|
||||
dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!info->ManufacturerName[0])
|
||||
strncpy(info->ManufacturerName, DEF_MFG_NAME,
|
||||
2 * MAX1721X_REG_MFG_NUMB);
|
||||
|
||||
if (get_string(info, MAX1721X_REG_DEV_STR,
|
||||
MAX1721X_REG_DEV_NUMB, info->DeviceName)) {
|
||||
dev_err(info->w1_dev, "Can't read device. Hardware error.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!info->DeviceName[0]) {
|
||||
unsigned int dev_name;
|
||||
|
||||
if (regmap_read(info->regmap,
|
||||
MAX172XX_REG_DEVNAME, &dev_name)) {
|
||||
dev_err(info->w1_dev, "Can't read device name reg.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (dev_name & MAX172XX_DEV_MASK) {
|
||||
case MAX172X1_DEV:
|
||||
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211,
|
||||
2 * MAX1721X_REG_DEV_NUMB);
|
||||
break;
|
||||
case MAX172X5_DEV:
|
||||
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215,
|
||||
2 * MAX1721X_REG_DEV_NUMB);
|
||||
break;
|
||||
default:
|
||||
strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN,
|
||||
2 * MAX1721X_REG_DEV_NUMB);
|
||||
}
|
||||
}
|
||||
|
||||
if (get_sn_string(info, info->SerialNumber)) {
|
||||
dev_err(info->w1_dev, "Can't read serial. Hardware error.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(info->bat)) {
|
||||
dev_err(info->w1_dev, "failed to register battery\n");
|
||||
return PTR_ERR(info->bat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct w1_family_ops w1_max1721x_fops = {
|
||||
.add_slave = devm_w1_max1721x_add_device,
|
||||
};
|
||||
|
||||
static struct w1_family w1_max1721x_family = {
|
||||
.fid = W1_MAX1721X_FAMILY_ID,
|
||||
.fops = &w1_max1721x_fops,
|
||||
};
|
||||
|
||||
module_w1_family(w1_max1721x_family);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alex A. Mihaylov <minimumlaw@rambler.ru>");
|
||||
MODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver");
|
||||
MODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID));
|
||||
@@ -535,7 +535,7 @@ 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,
|
||||
@@ -559,7 +559,7 @@ static ssize_t olpc_bat_error_read(struct device *dev,
|
||||
return sprintf(buf, "%d\n", ec_byte);
|
||||
}
|
||||
|
||||
static struct device_attribute olpc_bat_error = {
|
||||
static const struct device_attribute olpc_bat_error = {
|
||||
.attr = {
|
||||
.name = "error",
|
||||
.mode = S_IRUGO,
|
||||
|
||||
@@ -254,7 +254,7 @@ static struct attribute *pcf50633_mbc_sysfs_entries[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group mbc_attr_group = {
|
||||
static const struct attribute_group mbc_attr_group = {
|
||||
.name = NULL, /* put in device directory */
|
||||
.attrs = pcf50633_mbc_sysfs_entries,
|
||||
};
|
||||
|
||||
@@ -259,18 +259,14 @@ static int power_supply_check_supplies(struct power_supply *psy)
|
||||
/* All supplies found, allocate char ** array for filling */
|
||||
psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from),
|
||||
GFP_KERNEL);
|
||||
if (!psy->supplied_from) {
|
||||
dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
|
||||
if (!psy->supplied_from)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*psy->supplied_from = devm_kzalloc(&psy->dev,
|
||||
sizeof(char *) * (cnt - 1),
|
||||
GFP_KERNEL);
|
||||
if (!*psy->supplied_from) {
|
||||
dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
|
||||
if (!*psy->supplied_from)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return power_supply_populate_supplied_from(psy);
|
||||
}
|
||||
@@ -314,11 +310,12 @@ static int __power_supply_am_i_supplied(struct device *dev, void *_data)
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
struct psy_am_i_supplied_data *data = _data;
|
||||
|
||||
data->count++;
|
||||
if (__power_supply_is_supplied_by(epsy, data->psy))
|
||||
if (__power_supply_is_supplied_by(epsy, data->psy)) {
|
||||
data->count++;
|
||||
if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
|
||||
&ret))
|
||||
return ret.intval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -374,6 +371,47 @@ int power_supply_is_system_supplied(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
|
||||
|
||||
static int __power_supply_get_supplier_max_current(struct device *dev,
|
||||
void *data)
|
||||
{
|
||||
union power_supply_propval ret = {0,};
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
struct power_supply *psy = data;
|
||||
|
||||
if (__power_supply_is_supplied_by(epsy, psy))
|
||||
if (!epsy->desc->get_property(epsy,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
&ret))
|
||||
return ret.intval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
|
||||
{
|
||||
union power_supply_propval val = {0,};
|
||||
int curr;
|
||||
|
||||
if (!psy->desc->set_property)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* This function is not intended for use with a supply with multiple
|
||||
* suppliers, we simply pick the first supply to report a non 0
|
||||
* max-current.
|
||||
*/
|
||||
curr = class_for_each_device(power_supply_class, NULL, psy,
|
||||
__power_supply_get_supplier_max_current);
|
||||
if (curr <= 0)
|
||||
return (curr == 0) ? -ENODEV : curr;
|
||||
|
||||
val.intval = curr;
|
||||
|
||||
return psy->desc->set_property(psy,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
|
||||
|
||||
int power_supply_set_battery_charged(struct power_supply *psy)
|
||||
{
|
||||
if (atomic_read(&psy->use_cnt) >= 0 &&
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user