X-Power AXP288 PMIC Fuel Gauge Driver
Subsystems (4)
v3 cover X-Power AXP288 PMIC Fuel Gauge Driver
New power_supply driver at driver/power which interfaces with the axp20x mfd driver as a cell. Provides battery info, monitors for changes, and generates alerts on temperature and capacity issues
Todd Brandt (3):
mfd/axp20x: change battery cell name to fuel gauge mfd/axp20x: add support for fuel gauge cell X-Power AXP288 PMIC Fuel Gauge Driver
v1: 01/07/14
v2: 01/22/14
- replaced the const in axp20x_acpi_match declaration
- reformatted the commit text
- added IIO as driver dependency
- moved debug info to debugfs in file fuelgauge
- added HEALTH_COLD value to health calculation
- added better cleanup to remove call
cancelling any delayed work calls unregistration of the power_supply
v3: 01/28/14
- moved debugs create and init config regs after error case
- freed the virqs in the remove function
- remove .owner field as it is populated automatically
drivers/mfd/axp20x.c | 8 +- drivers/power/Kconfig | 9 + drivers/power/Makefile | 1 + drivers/power/axp288_fuel_gauge.c | 1151 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 43 +- 5 files changed, 1206 insertions(+), 6 deletions(-) create mode 100644 drivers/power/axp288_fuel_gauge.c
Signature · 1 line
1.9.1
PATCH RESEND v3 1/3 mfd/axp20x: change battery cell name to fuel gauge
Name changes to the battery cell structure to a more generic cell type: fuel gauge.
--- drivers/mfd/axp20x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
v1: 01/07/14
v2: 01/22/14
- replaced the const in axp20x_acpi_match declaration
v3: same as v2
drivers/mfd/axp20x.c +4 -4
@@ -87,7 +87,7 @@ static struct resource axp20x_pek_resources[] = { }, }; -static struct resource axp288_battery_resources[] = { +static struct resource axp288_fuel_gauge_resources[] = { { .start =AXP288_IRQ_QWBTU , .end =AXP288_IRQ_QWBTU ,
@@ -350,9 +350,9 @@ static struct mfd_cell axp288_cells[] = { .resources = axp288_charger_resources, }, { - .name = "axp288_battery", - .num_resources = ARRAY_SIZE(axp288_battery_resources), - .resources = axp288_battery_resources, + .name = "axp288_fuel_gauge", + .num_resources = ARRAY_SIZE(axp288_fuel_gauge_resources), + .resources = axp288_fuel_gauge_resources, }, { .name = "axp288_pmic_acpi",
Signature · 1 line
1.9.1
Lee Jones
AppliedRe: [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge
Quoted 9 lines · Todd Brandt
Name changes to the battery cell structure to a more generic cell type: fuel gauge.
Signed-off-by Todd Brandt <[email protected]>Acked-by Jacob Pan <[email protected]>--- drivers/mfd/axp20x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
Applied, thanks.
Quoted 4 lines
v1: 01/07/14
v2: 01/22/14
- replaced the const inaxp20x_acpi_matchdeclaration
v3: same as v2
FYI: Changelogs are usually placed _above_ the diff stat.
Signature · 4 lines
Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog
PATCH RESEND v3 2/3 mfd/axp20x: add support for fuel gauge cell
In reply to Todd BrandtRegister definitions and platform data structure for fuel gauge cell devices.
--- include/linux/mfd/axp20x.h | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-)
v3: same as v1-2 and is already accepted, re-included for continuity
include/linux/mfd/axp20x.h +41 -2
@@ -124,10 +124,27 @@ enum {#define AXP288_PMIC_ADC_H 0x56#define AXP288_PMIC_ADC_L 0x57#define AXP288_ADC_TS_PIN_CTRL 0x84 -#define AXP288_PMIC_ADC_EN 0x84 -#define AXP288_FG_TUNE5 0xed +/* Fuel Gauge */ +#define AXP288_FG_RDC1_REG 0xba +#define AXP288_FG_RDC0_REG 0xbb +#define AXP288_FG_OCVH_REG 0xbc +#define AXP288_FG_OCVL_REG 0xbd +#define AXP288_FG_OCV_CURVE_REG 0xc0 +#define AXP288_FG_DES_CAP1_REG 0xe0 +#define AXP288_FG_DES_CAP0_REG 0xe1 +#define AXP288_FG_CC_MTR1_REG 0xe2 +#define AXP288_FG_CC_MTR0_REG 0xe3 +#define AXP288_FG_OCV_CAP_REG 0xe4 +#define AXP288_FG_CC_CAP_REG 0xe5 +#define AXP288_FG_LOW_CAP_REG 0xe6 +#define AXP288_FG_TUNE0 0xe8 +#define AXP288_FG_TUNE1 0xe9 +#define AXP288_FG_TUNE2 0xea +#define AXP288_FG_TUNE3 0xeb +#define AXP288_FG_TUNE4 0xec +#define AXP288_FG_TUNE5 0xed/* Regulators IDs */ enum {
@@ -236,4 +253,26 @@ struct axp20x_dev { const struct regmap_irq_chip *regmap_irq_chip; }; +#define BATTID_LEN 64 +#define OCV_CURVE_SIZE 32 +#define MAX_THERM_CURVE_SIZE 25 +#define PD_DEF_MIN_TEMP 0 +#define PD_DEF_MAX_TEMP 55 + +struct axp20x_fg_pdata { +char battid[BATTID_LEN +1 ]; +int design_cap; +int min_volt; +int max_volt; +int max_temp; +int min_temp; +int cap1; +int cap0; +int rdc1; +int rdc0; +int ocv_curve[OCV_CURVE_SIZE ]; +int tcsz; +int thermistor_curve[MAX_THERM_CURVE_SIZE ][2 ]; +}; +#endif /* __LINUX_MFD_AXP20X_H */
Signature · 1 line
1.9.1
PATCH RESEND v3 3/3 X-Power AXP288 PMIC Fuel Gauge Driver
In reply to Todd BrandtNew power_supply driver at driver/power which interfaces with the axp20x mfd driver as a cell. Provides battery info, monitors for changes, and generates alerts on temperature and capacity issues
--- drivers/power/Kconfig | 9 + drivers/power/Makefile | 1 + drivers/power/axp288_fuel_gauge.c | 1151 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1161 insertions(+) create mode 100644 drivers/power/axp288_fuel_gauge.c
v1: 01/07/14
v2: 01/22/14
- reformatted the commit text
- added IIO as driver dependency
- moved debug info to debugfs in file fuelgauge
- added HEALTH_COLD value to health calculation
- added better cleanup to remove call
cancelling any delayed work calls unregistration of the power_supply
v3: 01/28/14
- moved debugs create and init config regs after error case
- freed the virqs in the remove function
- remove .owner field as it is populated automatically
drivers/power/Kconfig +9 -0
@@ -192,6 +192,15 @@ config BATTERY_DA9052 Say Y here to enable support for batteries charger integrated into DA9052 PMIC. +config AXP288_FUEL_GAUGE + tristate "X-Powers AXP288 Fuel Gauge" + depends on MFD_AXP20X && IIO + help + Say yes here to have support for X-Power power management IC (PMIC) + Fuel Gauge. The device provides battery statistics and status + monitoring as well as alerts for battery over/under voltage and + over/under temperature. + config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C
drivers/power/Makefile +1 -0
@@ -59,3 +59,4 @@ obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_POWER_RESET) += reset/ +obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
drivers/power/axp288_fuel_gauge.c new file +1151 -0
1,151 lines not on this page · raw message
Signature · 1 line
1.9.1
Hi Dmitry and Sebastian, can we get this driver included for 3.20?=0A= =0A=
Regards=0A=
Todd Brandt=0A=
=0A=
________________________________________=0A=
From: Todd Brandt [[email protected]]=0A=
Sent: Monday, February 02, 2015 12:44 PM=0A=
To: [email protected]; [email protected]; Woodhouse, David=0A=
Cc: [email protected]; [email protected]; linux-kernel@vger.= kernel.org; [email protected]; Brandt, Todd E; jacob.jun.pan@li= nux.intel.com; [email protected]; [email protected]; dwmw2@infradead= .org; [email protected]=0A=
Subject: [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver=0A= =0A=
New power_supply driver at driver/power which interfaces with the=0A= axp20x mfd driver as a cell. Provides battery info, monitors for=0A= changes, and generates alerts on temperature and capacity issues=0A= =0A=
---=0A=
drivers/power/Kconfig | 9 +=0A= drivers/power/Makefile | 1 +=0A= drivers/power/axp288_fuel_gauge.c | 1151 +++++++++++++++++++++++++++++++++= ++++=0A= 3 files changed, 1161 insertions(+)=0A= create mode 100644 drivers/power/axp288_fuel_gauge.c=0A=
=0A=
v1: 01/07/14=0A=
v2: 01/22/14=0A=
- reformatted the commit text=0A=
- added IIO as driver dependency=0A=
- moved debug info to debugfs in file fuelgauge=0A=
- added HEALTH_COLD value to health calculation=0A=
- added better cleanup to remove call=0A=
cancelling any delayed work calls=0A= unregistration of the power_supply=0A=
v3: 01/28/14=0A=
- moved debugs create and init config regs after error case=0A=
- freed the virqs in the remove function=0A=
- remove .owner field as it is populated automatically=0A= =0A=
drivers/power/Kconfig=0A= +0 -0
@@ -192,6 +192,15 @@ config BATTERY_DA9052=0A= Say Y here to enable support for batteries charger integrated int=
o=0A=
DA9052 PMIC.=0A= =0A= +config AXP288_FUEL_GAUGE=0A= + tristate "X-Powers AXP288 Fuel Gauge"=0A= + depends on MFD_AXP20X && IIO=0A= + help=0A=
+ Say yes here to have support for X-Power power management IC (PMI=
C)=0A=
+ Fuel Gauge. The device provides battery statistics and status=0A=
+ monitoring as well as alerts for battery over/under voltage and=
=0A=
+ over/under temperature.=0A=
+=0A=
config BATTERY_MAX17040=0A=
tristate "Maxim MAX17040 Fuel Gauge"=0A=
depends on I2C=0A=
drivers/power/Makefile=0A= +1 -0
@@ -59,3 +59,4 @@ obj-$(CONFIG_POWER_AVS) +=3D avs/=0A= obj-$(CONFIG_CHARGER_SMB347) +=3D smb347-charger.o=0A= obj-$(CONFIG_CHARGER_TPS65090) +=3D tps65090-charger.o=0A= obj-$(CONFIG_POWER_RESET) +=3D reset/=0A= +obj-$(CONFIG_AXP288_FUEL_GAUGE) +=3D axp288_fuel_gauge.o=0A=
drivers/power/axp288_fuel_= renamed from drivers/power/axp288_fuel_gauge.c +0 -0
gauge.c=0A=
new file mode 100644=0A=
index 0000000..c86e709=0A=
drivers/power/axp288_fuel_gauge.c=0A= renamed from dev/null=0A= +6 -0
@@ -0,0 +1,1151 @@=0A= +/*=0A= + * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver=0A= + *=0A= + * Copyright (C) 2014 Intel Corporation=0A= + *=0A= + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=
~~~=0A=
+ *=0A=
+ * This program is free software; you can redistribute it and/or modify=0A=
+ * it under the terms of the GNU General Public License as published by=0A=
+ * the Free Software Foundation; version 2 of the License.=0A=
+ *=0A=
+ * This program is distributed in the hope that it will be useful, but=0A=
+ * WITHOUT ANY WARRANTY; without even the implied warranty of=0A=
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU=
=0A=
+ * General Public License for more details.=0A=
+ *=0A=
+ */=0A=
+=0A=
+#include <linux/module.h>=0A=
+#include <linux/kernel.h>=0A=
+#include <linux/device.h>=0A=
+#include <linux/regmap.h>=0A=
+#include <linux/jiffies.h>=0A=
+#include <linux/interrupt.h>=0A=
+#include <linux/device.h>=0A=
+#include <linux/workqueue.h>=0A=
+#include <linux/mfd/axp20x.h>=0A=
+#include <linux/platform_device.h>=0A=
+#include <linux/power_supply.h>=0A=
+#include <linux/iio/consumer.h>=0A=
+#include <linux/debugfs.h>=0A=
+#include <linux/seq_file.h>=0A=
+=0A= +#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)=0A= +#define CHRG_STAT_BAT_VALID (1 << 4)=0A= +#define CHRG_STAT_BAT_PRESENT (1 << 5)=0A= +#define CHRG_STAT_CHARGING (1 << 6)=0A= +#define CHRG_STAT_PMIC_OTP (1 << 7)=0A= +=0A= +#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */=0A= +#define CHRG_CCCV_CC_BIT_POS 0=0A= +#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */=0A= +#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */=0A= +#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */= =0A= +#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */=0A= +#define CHRG_CCCV_CV_BIT_POS 5=0A= +#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */=0A= +#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */=0A= +#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */=0A= +#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */=0A= +#define CHRG_CCCV_CHG_EN (1 << 7)=0A= +=0A= +#define CV_4100 4100 /* =
4100mV */=0A=
+#define CV_4150 4150 /* =
4150mV */=0A=
+#define CV_4200 4200 /* =
4200mV */=0A=
+#define CV_4350 4350 /* =
4350mV */=0A=
+=0A=
+#define TEMP_IRQ_CFG_QWBTU (1 << 0)=0A=
+#define TEMP_IRQ_CFG_WBTU (1 << 1)=0A=
+#define TEMP_IRQ_CFG_QWBTO (1 << 2)=0A=
+#define TEMP_IRQ_CFG_WBTO (1 << 3)=0A=
+#define TEMP_IRQ_CFG_MASK 0xf=0A=
+=0A=
+#define FG_IRQ_CFG_LOWBATT_WL2 (1 << 0)=0A=
+#define FG_IRQ_CFG_LOWBATT_WL1 (1 << 1)=0A=
+#define FG_IRQ_CFG_LOWBATT_MASK 0x3=0A=
+#define LOWBAT_IRQ_STAT_LOWBATT_WL2 (1 << 0)=0A=
+#define LOWBAT_IRQ_STAT_LOWBATT_WL1 (1 << 1)=0A=
+=0A=
+#define FG_CNTL_OCV_ADJ_STAT (1 << 2)=0A=
+#define FG_CNTL_OCV_ADJ_EN (1 << 3)=0A=
+#define FG_CNTL_CAP_ADJ_STAT (1 << 4)=0A=
+#define FG_CNTL_CAP_ADJ_EN (1 << 5)=0A=
+#define FG_CNTL_CC_EN (1 << 6)=0A=
+#define FG_CNTL_GAUGE_EN (1 << 7)=0A=
+=0A=
+#define FG_REP_CAP_VALID (1 << 7)=0A=
+#define FG_REP_CAP_VAL_MASK 0x7F=0A=
+=0A=
+#define FG_DES_CAP1_VALID (1 << 7)=0A=
+#define FG_DES_CAP1_VAL_MASK 0x7F=0A=
+#define FG_DES_CAP0_VAL_MASK 0xFF=0A=
+#define FG_DES_CAP_RES_LSB 1456 /* 1.456mAhr */=0A=
+=0A=
+#define FG_CC_MTR1_VALID (1 << 7)=0A=
+#define FG_CC_MTR1_VAL_MASK 0x7F=0A=
+#define FG_CC_MTR0_VAL_MASK 0xFF=0A=
+#define FG_DES_CC_RES_LSB 1456 /* 1.456mAhr */=0A=
+=0A=
+#define FG_OCV_CAP_VALID (1 << 7)=0A=
+#define FG_OCV_CAP_VAL_MASK 0x7F=0A=
+#define FG_CC_CAP_VALID (1 << 7)=0A=
+#define FG_CC_CAP_VAL_MASK 0x7F=0A=
+=0A=
+#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */=0A=
+#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */=0A=
+#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */=0A=
+#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */=0A=
+#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */=0A=
+#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */=0A=
+=0A=
+#define STATUS_MON_DELAY_JIFFIES (HZ * 60) /*60 sec */=0A=
+#define NR_RETRY_CNT 3=0A=
+#define DEV_NAME "axp288_fuel_gauge"=0A=
+=0A=
+/* 1.1mV per LSB expressed in uV */=0A=
+#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)=0A=
+/* properties converted to tenths of degrees, uV, uA, uW */=0A=
+#define PROP_TEMP(a) ((a) * 10)=0A=
+#define UNPROP_TEMP(a) ((a) / 10)=0A=
+#define PROP_VOLT(a) ((a) * 1000)=0A=
+#define PROP_CURR(a) ((a) * 1000)=0A=
+=0A=
+#define AXP288_FG_INTR_NUM 6=0A=
+enum {=0A=
+ QWBTU_IRQ =3D 0,=0A=
+ WBTU_IRQ,=0A=
+ QWBTO_IRQ,=0A=
+ WBTO_IRQ,=0A=
+ WL2_IRQ,=0A=
+ WL1_IRQ,=0A=
+};=0A=
+=0A=
+struct axp288_fg_info {=0A=
+ struct platform_device *pdev;=0A=
+ struct axp20x_fg_pdata *pdata;=0A=
+ struct regmap *regmap;=0A=
+ struct regmap_irq_chip_data *regmap_irqc;=0A=
+ int irq[AXP288_FG_INTR_NUM];=0A=
+ struct power_supply bat;=0A=
+ struct mutex lock;=0A=
+ int status;=0A=
+ struct delayed_work status_monitor;=0A=
+ struct dentry *debug_file;=0A=
+};=0A=
+=0A=
+static enum power_supply_property fuel_gauge_props[] =3D {=0A=
+ POWER_SUPPLY_PROP_STATUS,=0A=
+ POWER_SUPPLY_PROP_PRESENT,=0A=
+ POWER_SUPPLY_PROP_HEALTH,=0A=
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,=0A=
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,=0A=
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,=0A=
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,=0A=
+ POWER_SUPPLY_PROP_CURRENT_NOW,=0A=
+ POWER_SUPPLY_PROP_CAPACITY,=0A=
+ POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,=0A=
+ POWER_SUPPLY_PROP_TEMP,=0A=
+ POWER_SUPPLY_PROP_TEMP_MAX,=0A=
+ POWER_SUPPLY_PROP_TEMP_MIN,=0A=
+ POWER_SUPPLY_PROP_TEMP_ALERT_MIN,=0A=
+ POWER_SUPPLY_PROP_TEMP_ALERT_MAX,=0A=
+ POWER_SUPPLY_PROP_TECHNOLOGY,=0A=
+ POWER_SUPPLY_PROP_CHARGE_FULL,=0A=
+ POWER_SUPPLY_PROP_CHARGE_NOW,=0A=
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,=0A=
+ POWER_SUPPLY_PROP_MODEL_NAME,=0A=+};=0A=
+=0A=
+static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)=0A= +{=0A=
+ int ret, i;=0A=
+ unsigned int val;=0A=
+=0A=
+ for (i =3D 0; i < NR_RETRY_CNT; i++) {=0A=
+ ret =3D regmap_read(info->regmap, reg, &val);=0A=
+ if (ret =3D=3D -EBUSY)=0A=
+ continue;=0A=
+ else=0A=
+ break;=0A=
+ }=0A=
+=0A=
+ if (ret < 0)=0A=
+ dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);=
=0A=
+=0A=
+ return val;=0A=+}=0A=
+=0A=
+static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 = val)=0A=
+{=0A=
+ int ret;=0A=
+=0A= + ret =3D regmap_write(info->regmap, reg, (unsigned int)val);=0A= +=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret)= ;=0A= +=0A= + return ret;=0A=
+}=0A=
+=0A=
+static int pmic_read_adc_val(const char *name, int *raw_val,=0A=
+ struct axp288_fg_info *info)=0A=
+{=0A=
+ int ret, val =3D 0;=0A=
+ struct iio_channel *indio_chan;=0A=
+=0A=
+ indio_chan =3D iio_channel_get(NULL, name);=0A=
+ if (IS_ERR_OR_NULL(indio_chan)) {=0A=
+ ret =3D PTR_ERR(indio_chan);=0A=
+ goto exit;=0A=
+ }=0A=
+ ret =3D iio_read_channel_raw(indio_chan, &val);=0A=
+ if (ret < 0) {=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "IIO channel read error: %x, %x\n", ret, val);=0A=
+ goto err_exit;=0A=
+ }=0A=
+=0A=
+ dev_dbg(&info->pdev->dev, "adc raw val=3D%x\n", val);=0A=
+ *raw_val =3D val;=0A=
+=0A=+err_exit:=0A=
+ iio_channel_release(indio_chan);=0A=
+exit:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+#ifdef CONFIG_DEBUG_FS=0A=
+static int fuel_gauge_debug_show(struct seq_file *s, void *data)=0A= +{=0A=
+ struct axp288_fg_info *info =3D s->private;=0A=
+ int raw_val, ret;=0A=
+=0A=
+ seq_printf(s, " PWR_STATUS[%02x] : %02x\n",=0A=
+ AXP20X_PWR_INPUT_STATUS,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));=0A=
+ seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",=0A=
+ AXP20X_PWR_OP_MODE,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));=0A=
+ seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",=0A=
+ AXP20X_CHRG_CTRL1,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));=0A=
+ seq_printf(s, " VLTF[%02x] : %02x\n",=0A=
+ AXP20X_V_LTF_DISCHRG,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));=0A=
+ seq_printf(s, " VHTF[%02x] : %02x\n",=0A=
+ AXP20X_V_HTF_DISCHRG,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));=0A=
+ seq_printf(s, " CC_CTRL[%02x] : %02x\n",=0A=
+ AXP20X_CC_CTRL,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));=0A=
+ seq_printf(s, "BATTERY CAP[%02x] : %02x\n",=0A=
+ AXP20X_FG_RES,=0A=
+ fuel_gauge_reg_readb(info, AXP20X_FG_RES));=0A=
+ seq_printf(s, " FG_RDC1[%02x] : %02x\n",=0A=
+ AXP288_FG_RDC1_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));=0A=
+ seq_printf(s, " FG_RDC0[%02x] : %02x\n",=0A=
+ AXP288_FG_RDC0_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));=0A=
+ seq_printf(s, " FG_OCVH[%02x] : %02x\n",=0A=
+ AXP288_FG_OCVH_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));=0A=
+ seq_printf(s, " FG_OCVL[%02x] : %02x\n",=0A=
+ AXP288_FG_OCVL_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));=0A=
+ seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",=0A=
+ AXP288_FG_DES_CAP1_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG));=0A=
+ seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",=0A=
+ AXP288_FG_DES_CAP0_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG));=0A=
+ seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n",=0A=
+ AXP288_FG_CC_MTR1_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG));=0A=
+ seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n",=0A=
+ AXP288_FG_CC_MTR0_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG));=0A=
+ seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",=0A=
+ AXP288_FG_OCV_CAP_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));=0A=
+ seq_printf(s, " FG_CC_CAP[%02x] : %02x\n",=0A=
+ AXP288_FG_CC_CAP_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));=0A=
+ seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",=0A=
+ AXP288_FG_LOW_CAP_REG,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));=0A=
+ seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE0,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));=0A=
+ seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE1,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));=0A=
+ seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE2,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));=0A=
+ seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE3,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));=0A=
+ seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE4,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));=0A=
+ seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",=0A=
+ AXP288_FG_TUNE5,=0A=
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));=0A=
+=0A=
+ ret =3D pmic_read_adc_val("axp288-batt-temp", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-batttemp : %d\n", raw_val);=0A=
+ ret =3D pmic_read_adc_val("axp288-pmic-temp", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-pmictemp : %d\n", raw_val);=0A=
+ ret =3D pmic_read_adc_val("axp288-system-temp", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-systtemp : %d\n", raw_val);=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-curr", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);=0A=
+ ret =3D pmic_read_adc_val("axp288-batt-volt", &raw_val, info);=0A=
+ if (ret >=3D 0)=0A=
+ seq_printf(s, "axp288-battvolt : %d\n", raw_val);=0A=
+=0A=
+ return 0;=0A=+}=0A=
+=0A=
+static int debug_open(struct inode *inode, struct file *file)=0A= +{=0A=
+ return single_open(file, fuel_gauge_debug_show, inode->i_private);=
=0A=
+}=0A=
+=0A=
+static const struct file_operations fg_debug_fops =3D {=0A=
+ .open =3D debug_open,=0A= + .read =3D seq_read,=0A= + .llseek =3D seq_lseek,=0A= + .release =3D single_release,=0A=
+};=0A=
+=0A=
+static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)=0A= +{=0A=
+ info->debug_file =3D debugfs_create_file("fuelgauge", 0666, NULL,=
=0A=
+ info, &fg_debug_fops);=0A=
+}=0A=
+=0A=
+static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)=0A= +{=0A=
+ debugfs_remove(info->debug_file);=0A=
+}=0A=
+#else=0A=
+static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)= =0A=
+{=0A=
+}=0A=
+static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)= =0A=
+{=0A=
+}=0A=
+#endif=0A=
+=0A=
+static void fuel_gauge_get_status(struct axp288_fg_info *info)=0A= +{=0A=
+ int pwr_stat, ret;=0A=
+ int charge, discharge;=0A=
+=0A= + pwr_stat =3D fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);=
=0A=
+ if (pwr_stat < 0) {=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "PWR STAT read failed:%d\n", pwr_stat);=0A=
+ return;=0A=
+ }=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-curr", &charge, info);=0A=
+ if (ret < 0) {=0A=+ dev_err(&info->pdev->dev,=0A=
+ "ADC charge current read failed:%d\n", ret);=0A=
+ return;=0A=
+ }=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);=
=0A=
+ if (ret < 0) {=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "ADC discharge current read failed:%d\n", ret);=0A=
+ return;=0A=
+ }=0A=
+=0A=
+ if (charge > 0)=0A=
+ info->status =3D POWER_SUPPLY_STATUS_CHARGING;=0A=
+ else if (discharge > 0)=0A=
+ info->status =3D POWER_SUPPLY_STATUS_DISCHARGING;=0A=
+ else {=0A=
+ if (pwr_stat & CHRG_STAT_BAT_PRESENT)=0A=
+ info->status =3D POWER_SUPPLY_STATUS_FULL;=0A=
+ else=0A=
+ info->status =3D POWER_SUPPLY_STATUS_NOT_CHARGING;==0A=
+ }=0A=
+}=0A=
+=0A=
+static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)= =0A=
+{=0A=
+ int ret =3D 0, raw_val;=0A=
+=0A=
+ ret =3D pmic_read_adc_val("axp288-batt-volt", &raw_val, info);=0A=
+ if (ret < 0)=0A=
+ goto vbatt_read_fail;=0A=
+=0A=
+ *vbatt =3D VOLTAGE_FROM_ADC(raw_val);=0A=+vbatt_read_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)= =0A=
+{=0A=
+ int ret, value =3D 0;=0A=
+ int charge, discharge;=0A=
+=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-curr", &charge, info);=0A=
+ if (ret < 0)=0A=
+ goto current_read_fail;=0A=
+ ret =3D pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);=
=0A=
+ if (ret < 0)=0A=
+ goto current_read_fail;=0A=
+=0A=
+ if (charge > 0)=0A=
+ value =3D charge;=0A=
+ else if (discharge > 0)=0A=
+ value =3D -1 * discharge;=0A=
+=0A=
+ *cur =3D value;=0A=+current_read_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int temp_to_adc(struct axp288_fg_info *info, int tval)=0A= +{=0A=
+ int rntc =3D 0, i, ret, adc_val;=0A=
+ int rmin, rmax, tmin, tmax;=0A=
+ int tcsz =3D info->pdata->tcsz;=0A=
+=0A=
+ /* get the Rntc resitance value for this temp */=0A=
+ if (tval > info->pdata->thermistor_curve[0][1]) {=0A=
+ rntc =3D info->pdata->thermistor_curve[0][0];=0A=
+ } else if (tval <=3D info->pdata->thermistor_curve[tcsz-1][1]) {=0A=
+ rntc =3D info->pdata->thermistor_curve[tcsz-1][0];=0A=
+ } else {=0A=
+ for (i =3D 1; i < tcsz; i++) {=0A=
+ if (tval > info->pdata->thermistor_curve[i][1]) {==0A=
+ rmin =3D info->pdata->thermistor_curve[i-1]=
[0];=0A=
+ rmax =3D info->pdata->thermistor_curve[i][0=
];=0A=
+ tmin =3D info->pdata->thermistor_curve[i-1]=
[1];=0A=
+ tmax =3D info->pdata->thermistor_curve[i][1=
];=0A=
+ rntc =3D rmin + ((rmax - rmin) *=0A=
+ (tval - tmin) / (tmax - tmin));=0A=
+ break;=0A=
+ }=0A=
+ }=0A=
+ }=0A=
+=0A=
+ /* we need the current to calculate the proper adc voltage */=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);=0A=
+ if (ret < 0) {=0A=
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret=
);=0A=
+ ret =3D 0x30;=0A=
+ }=0A=
+=0A=
+ /*=0A=
+ * temperature is proportional to NTS thermistor resistance=0A=
+ * ADC_RATE[5-4] determines current, 00=3D20uA,01=3D40uA,10=3D60uA,=11=3D80uA=0A=
+ * [12-bit ADC VAL] =3D R_NTC(=D9) * current / 800=0A=
+ */=0A=
+ adc_val =3D rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800;=0A=
+=0A= + return adc_val;=0A=
+}=0A=
+=0A=
+static int adc_to_temp(struct axp288_fg_info *info, int adc_val)=0A= +{=0A=
+ int ret, r, i, tval =3D 0;=0A=
+ int rmin, rmax, tmin, tmax;=0A=
+ int tcsz =3D info->pdata->tcsz;=0A=
+=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);=0A=
+ if (ret < 0) {=0A=
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret=
);=0A=
+ ret =3D 0x30;=0A=
+ }=0A=
+=0A=
+ /*=0A=
+ * temperature is proportional to NTS thermistor resistance=0A=
+ * ADC_RATE[5-4] determines current, 00=3D20uA,01=3D40uA,10=3D60uA,=11=3D80uA=0A=
+ * R_NTC(=D9) =3D [12-bit ADC VAL] * 800 / current=0A=
+ */=0A=
+ r =3D adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3)));=0A=
+=0A=
+ if (r < info->pdata->thermistor_curve[0][0]) {=0A=
+ tval =3D info->pdata->thermistor_curve[0][1];=0A=
+ } else if (r >=3D info->pdata->thermistor_curve[tcsz-1][0]) {=0A=
+ tval =3D info->pdata->thermistor_curve[tcsz-1][1];=0A=
+ } else {=0A=
+ for (i =3D 1; i < tcsz; i++) {=0A=
+ if (r < info->pdata->thermistor_curve[i][0]) {=0A=
+ rmin =3D info->pdata->thermistor_curve[i-1]=[0];=0A=
+ rmax =3D info->pdata->thermistor_curve[i][0=
];=0A=
+ tmin =3D info->pdata->thermistor_curve[i-1]=
[1];=0A=
+ tmax =3D info->pdata->thermistor_curve[i][1=
];=0A=
+ tval =3D tmin + ((tmax - tmin) *=0A=
+ (r - rmin) / (rmax - rmin));=0A=
+ break;=0A=
+ }=0A=
+ }=0A=
+ }=0A=
+=0A= + return tval;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp)= =0A=
+{=0A=
+ int ret, raw_val =3D 0;=0A=
+=0A=
+ ret =3D pmic_read_adc_val("axp288-batt-temp", &raw_val, info);=0A=
+ if (ret < 0)=0A=
+ goto temp_read_fail;=0A=
+=0A=
+ *btemp =3D adc_to_temp(info, raw_val);=0A=
+=0A=+temp_read_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)=0A= +{=0A=
+ int ret, value;=0A=
+=0A= + /* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */=0A= + ret =3D fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);=0A= + if (ret < 0)=0A= + goto vocv_read_fail;=0A= + value =3D ret << 4;=0A= +=0A= + ret =3D fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);=0A= + if (ret < 0)=0A= + goto vocv_read_fail;=0A= + value |=3D (ret & 0xf);=0A= +=0A= + *vocv =3D VOLTAGE_FROM_ADC(value);=0A=
+vocv_read_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_battery_health(struct axp288_fg_info *info)=0A= +{=0A=
+ int temp, vocv;=0A=
+ int ret, health =3D POWER_SUPPLY_HEALTH_UNKNOWN;=0A=
+=0A= + ret =3D fuel_gauge_get_btemp(info, &temp);=0A= + if (ret < 0)=0A= + goto health_read_fail;=0A= +=0A= + ret =3D fuel_gauge_get_vocv(info, &vocv);=0A= + if (ret < 0)=0A= + goto health_read_fail;=0A= +=0A= + if (vocv > info->pdata->max_volt)=0A= + health =3D POWER_SUPPLY_HEALTH_OVERVOLTAGE;=0A= + else if (temp > info->pdata->max_temp)=0A= + health =3D POWER_SUPPLY_HEALTH_OVERHEAT;=0A= + else if (temp < info->pdata->min_temp)=0A= + health =3D POWER_SUPPLY_HEALTH_COLD;=0A= + else if (vocv < info->pdata->min_volt)=0A= + health =3D POWER_SUPPLY_HEALTH_DEAD;=0A= + else=0A= + health =3D POWER_SUPPLY_HEALTH_GOOD;=0A= +=0A=
+health_read_fail:=0A=
+ return health;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret, adc_val;=0A=
+=0A= + /* program temperature threshold as 1/16 ADC value */=0A= + adc_val =3D temp_to_adc(info, info->pdata->max_temp);=0A= + ret =3D fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >=
Quoted 1 line
4);=0A=
+=0A= + return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret, adc_val;=0A=
+=0A= + /* program temperature threshold as 1/16 ADC value */=0A= + adc_val =3D temp_to_adc(info, info->pdata->min_temp);=0A= + ret =3D fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >=
Quoted 1 line
4);=0A=
+=0A= + return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_get_property(struct power_supply *ps,=0A=
+ enum power_supply_property prop,=0A=
+ union power_supply_propval *val)=0A=
+{=0A=
+ struct axp288_fg_info *info =3D container_of(ps,=0A=
+ struct axp288_fg_info, bat);=0A=
+ int ret =3D 0, value;=0A=
+=0A=
+ mutex_lock(&info->lock);=0A=
+ switch (prop) {=0A=
+ case POWER_SUPPLY_PROP_STATUS:=0A=
+ fuel_gauge_get_status(info);=0A=
+ val->intval =3D info->status;=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_HEALTH:=0A=
+ val->intval =3D fuel_gauge_battery_health(info);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:=0A=
+ ret =3D fuel_gauge_get_vbatt(info, &value);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+ val->intval =3D PROP_VOLT(value);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:=0A=
+ ret =3D fuel_gauge_get_vocv(info, &value);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+ val->intval =3D PROP_VOLT(value);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_CURRENT_NOW:=0A=
+ ret =3D fuel_gauge_get_current(info, &value);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+ val->intval =3D PROP_CURR(value);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_PRESENT:=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+=0A=
+ if (ret & CHRG_STAT_BAT_PRESENT)=0A=
+ val->intval =3D 1;=0A=
+ else=0A=
+ val->intval =3D 0;=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_CAPACITY:=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_FG_RES);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+=0A=
+ if (!(ret & FG_REP_CAP_VALID))=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "capacity measurement not valid\n");=0A=
+ val->intval =3D (ret & FG_REP_CAP_VAL_MASK);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);==0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+ val->intval =3D (ret & 0x0f);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TEMP:=0A=
+ ret =3D fuel_gauge_get_btemp(info, &value);=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+ val->intval =3D PROP_TEMP(value);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TEMP_MAX:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:=0A=
+ val->intval =3D PROP_TEMP(info->pdata->max_temp);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TEMP_MIN:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:=0A=
+ val->intval =3D PROP_TEMP(info->pdata->min_temp);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TECHNOLOGY:=0A=
+ val->intval =3D POWER_SUPPLY_TECHNOLOGY_LION;=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_CHARGE_NOW:=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG);=
=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+=0A= + value =3D (ret & FG_CC_MTR1_VAL_MASK) << 8;=0A= + ret =3D fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG);= =0A= + if (ret < 0)=0A= + goto fuel_gauge_read_err;=0A= + value |=3D (ret & FG_CC_MTR0_VAL_MASK);=0A= + val->intval =3D value * FG_DES_CAP_RES_LSB;=0A= + break;=0A=
+ case POWER_SUPPLY_PROP_CHARGE_FULL:=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);=
=0A=
+ if (ret < 0)=0A=
+ goto fuel_gauge_read_err;=0A=
+=0A= + value =3D (ret & FG_DES_CAP1_VAL_MASK) << 8;=0A= + ret =3D fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG);= =0A= + if (ret < 0)=0A= + goto fuel_gauge_read_err;=0A= + value |=3D (ret & FG_DES_CAP0_VAL_MASK);=0A= + val->intval =3D value * FG_DES_CAP_RES_LSB;=0A= + break;=0A=
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:=0A=
+ val->intval =3D PROP_CURR(info->pdata->design_cap);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:=0A=
+ val->intval =3D PROP_VOLT(info->pdata->max_volt);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:=0A=
+ val->intval =3D PROP_VOLT(info->pdata->min_volt);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_MODEL_NAME:=0A=
+ val->strval =3D info->pdata->battid;=0A=
+ break;=0A=
+ default:=0A=
+ mutex_unlock(&info->lock);=0A=
+ return -EINVAL;=0A=
+ }=0A=
+=0A= + mutex_unlock(&info->lock);=0A= + return 0;=0A= +=0A=
+fuel_gauge_read_err:=0A=
+ mutex_unlock(&info->lock);=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_set_property(struct power_supply *ps,=0A=
+ enum power_supply_property prop,=0A=
+ const union power_supply_propval *val)=0A=
+{=0A=
+ struct axp288_fg_info *info =3D container_of(ps,=0A=
+ struct axp288_fg_info, bat);=0A=
+ int ret =3D 0;=0A=
+=0A=
+ mutex_lock(&info->lock);=0A=
+ switch (prop) {=0A=
+ case POWER_SUPPLY_PROP_STATUS:=0A=
+ info->status =3D val->intval;=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TEMP_MIN:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:=0A=
+ if ((val->intval < PD_DEF_MIN_TEMP) ||=0A=
+ (val->intval > PD_DEF_MAX_TEMP)) {=0A=
+ ret =3D -EINVAL;=0A=
+ break;=0A=
+ }=0A=
+ info->pdata->min_temp =3D UNPROP_TEMP(val->intval);=0A=
+ ret =3D fuel_gauge_set_low_btemp_alert(info);=0A=
+ if (ret < 0)=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "temp alert min set fail:%d\n", ret);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_TEMP_MAX:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:=0A=
+ if ((val->intval < PD_DEF_MIN_TEMP) ||=0A=
+ (val->intval > PD_DEF_MAX_TEMP)) {=0A=
+ ret =3D -EINVAL;=0A=
+ break;=0A=
+ }=0A=
+ info->pdata->max_temp =3D UNPROP_TEMP(val->intval);=0A=
+ ret =3D fuel_gauge_set_high_btemp_alert(info);=0A=
+ if (ret < 0)=0A=
+ dev_err(&info->pdev->dev,=0A=
+ "temp alert max set fail:%d\n", ret);=0A=
+ break;=0A=
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:=0A=
+ if ((val->intval < 0) || (val->intval > 15)) {=0A=
+ ret =3D -EINVAL;=0A=
+ break;=0A=
+ }=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);=
=0A=
+ if (ret < 0)=0A=
+ break;=0A=
+ ret &=3D 0xf0;=0A=
+ ret |=3D (val->intval & 0xf);=0A=
+ ret =3D fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, =ret);=0A=
+ break;=0A=
+ default:=0A=
+ ret =3D -EINVAL;=0A=
+ break;=0A=
+ }=0A=
+=0A= + mutex_unlock(&info->lock);=0A= + return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_property_is_writeable(struct power_supply *psy,=0A=
+ enum power_supply_property psp)=0A=
+{=0A=
+ int ret;=0A=
+=0A=
+ switch (psp) {=0A=
+ case POWER_SUPPLY_PROP_STATUS:=0A=
+ case POWER_SUPPLY_PROP_TEMP_MIN:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:=0A=
+ case POWER_SUPPLY_PROP_TEMP_MAX:=0A=
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:=0A=
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:=0A=
+ ret =3D 1;=0A=
+ break;=0A=
+ default:=0A=
+ ret =3D 0;=0A=
+ }=0A=
+=0A=
+ return ret;=0A=+}=0A=
+=0A=
+static void fuel_gauge_status_monitor(struct work_struct *work)=0A= +{=0A=
+ struct axp288_fg_info *info =3D container_of(work,=0A=
+ struct axp288_fg_info, status_monitor.work);=0A=
+=0A= + fuel_gauge_get_status(info);=0A= + power_supply_changed(&info->bat);=0A= + schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFI=
ES);=0A=
+}=0A=
+=0A=
+static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)=0A= +{=0A=
+ struct axp288_fg_info *info =3D dev;=0A=
+ int i;=0A=
+=0A=
+ for (i =3D 0; i < AXP288_FG_INTR_NUM; i++) {=0A=
+ if (info->irq[i] =3D=3D irq)=0A=
+ break;=0A=
+ }=0A=
+=0A=
+ if (i >=3D AXP288_FG_INTR_NUM) {=0A=
+ dev_warn(&info->pdev->dev, "spurious interrupt!!\n");=0A=
+ return IRQ_NONE;=0A=
+ }=0A=
+=0A=
+ switch (i) {=0A=
+ case QWBTU_IRQ:=0A=
+ dev_info(&info->pdev->dev,=0A=
+ "Quit Battery under temperature in work mode IRQ (Q=WBTU)\n");=0A=
+ break;=0A=
+ case WBTU_IRQ:=0A=
+ dev_info(&info->pdev->dev,=0A=
+ "Battery under temperature in work mode IRQ (WBTU)\=
n");=0A=
+ break;=0A=
+ case QWBTO_IRQ:=0A=
+ dev_info(&info->pdev->dev,=0A=
+ "Quit Battery over temperature in work mode IRQ (QW=
BTO)\n");=0A=
+ break;=0A=
+ case WBTO_IRQ:=0A=
+ dev_info(&info->pdev->dev,=0A=
+ "Battery over temperature in work mode IRQ (WBTO)\n=
");=0A=
+ break;=0A=
+ case WL2_IRQ:=0A=
+ dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");=
=0A=
+ break;=0A=
+ case WL1_IRQ:=0A=
+ dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");=
=0A=
+ break;=0A=
+ default:=0A=
+ dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");=0A=
+ }=0A=
+=0A= + power_supply_changed(&info->bat);=0A= + return IRQ_HANDLED;=0A=
+}=0A=
+=0A=
+static void fuel_gauge_external_power_changed(struct power_supply *psy)=0A= +{=0A=
+ struct axp288_fg_info *info =3D container_of(psy,=0A=
+ struct axp288_fg_info, bat);=0A=
+=0A= + power_supply_changed(&info->bat);=0A=
+}=0A=
+=0A=
+static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info)= =0A=
+{=0A=
+ int ret;=0A=
+ u8 reg_val;=0A=
+=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_FG_RES);=0A=
+ if (ret < 0) {=0A=
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret=);=0A=
+ return ret;=0A=
+ }=0A=
+ ret =3D (ret & FG_REP_CAP_VAL_MASK);=0A=
+=0A= + if (ret > FG_LOW_CAP_WARN_THR)=0A= + reg_val =3D FG_LOW_CAP_WARN_THR;=0A= + else if (ret > FG_LOW_CAP_CRIT_THR)=0A= + reg_val =3D FG_LOW_CAP_CRIT_THR;=0A= + else=0A= + reg_val =3D FG_LOW_CAP_SHDN_THR;=0A= +=0A= + reg_val |=3D FG_LOW_CAP_THR1_VAL;=0A= + ret =3D fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val)=
;=0A=
+ if (ret < 0)=0A=
+ dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, re=
t);=0A=
+=0A= + return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret;=0A=
+ u8 val;=0A=
+=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);=0A=
+ if (ret < 0)=0A=
+ goto fg_prog_ocv_fail;=0A=
+ else=0A=
+ val =3D (ret & ~CHRG_CCCV_CV_MASK);=0A=
+=0A=
+ switch (info->pdata->max_volt) {=0A=
+ case CV_4100:=0A=
+ val |=3D (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);=0A=
+ break;=0A=
+ case CV_4150:=0A=
+ val |=3D (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);=0A=
+ break;=0A=
+ case CV_4200:=0A=
+ val |=3D (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);=0A=
+ break;=0A=
+ case CV_4350:=0A=
+ val |=3D (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);=0A=
+ break;=0A=
+ default:=0A=
+ val |=3D (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);=0A=
+ break;=0A=
+ }=0A=
+=0A=
+ ret =3D fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val);=0A=+fg_prog_ocv_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_program_design_cap(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret;=0A=
+=0A= + ret =3D fuel_gauge_reg_writeb(info,=0A= + AXP288_FG_DES_CAP1_REG, info->pdata->cap1);=0A= + if (ret < 0)=0A= + goto fg_prog_descap_fail;=0A= +=0A= + ret =3D fuel_gauge_reg_writeb(info,=0A= + AXP288_FG_DES_CAP0_REG, info->pdata->cap0);=0A= +=0A=
+fg_prog_descap_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret =3D 0, i;=0A=
+=0A=
+ for (i =3D 0; i < OCV_CURVE_SIZE; i++) {=0A=
+ ret =3D fuel_gauge_reg_writeb(info,=0A=
+ AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve=[i]);=0A=
+ if (ret < 0)=0A=
+ goto fg_prog_ocv_fail;=0A=
+ }=0A=
+=0A=
+fg_prog_ocv_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret;=0A=
+=0A= + ret =3D fuel_gauge_reg_writeb(info,=0A= + AXP288_FG_RDC1_REG, info->pdata->rdc1);=0A= + if (ret < 0)=0A= + goto fg_prog_ocv_fail;=0A= +=0A= + ret =3D fuel_gauge_reg_writeb(info,=0A= + AXP288_FG_RDC0_REG, info->pdata->rdc0);=0A= +=0A=
+fg_prog_ocv_fail:=0A=
+ return ret;=0A=
+}=0A=
+=0A=
+static void fuel_gauge_init_config_regs(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret;=0A=
+=0A=
+ /*=0A=
+ * check if the config data is already=0A=
+ * programmed and if so just return.=0A=
+ */=0A=
+=0A=
+ ret =3D fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);=0A=
+ if (ret < 0) {=0A=
+ dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");=0A=
+ } else if (!(ret & FG_DES_CAP1_VALID)) {=0A=
+ dev_info(&info->pdev->dev, "FG data needs to be initialized=\n");=0A=
+ } else {=0A=
+ dev_info(&info->pdev->dev, "FG data is already initialized\=
n");=0A=
+ return;=0A=
+ }=0A=
+=0A= + ret =3D fuel_gauge_program_vbatt_full(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);= =0A= +=0A= + ret =3D fuel_gauge_program_design_cap(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);= =0A= +=0A= + ret =3D fuel_gauge_program_rdc_vals(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);=0A= +=0A= + ret =3D fuel_gauge_program_ocv_curve(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);= =0A= +=0A= + ret =3D fuel_gauge_set_lowbatt_thresholds(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret)= ;=0A= +=0A= + ret =3D fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);=
=0A=
+}=0A=
+=0A=
+static void fuel_gauge_init_irq(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret, i, pirq;=0A=
+=0A=
+ for (i =3D 0; i < AXP288_FG_INTR_NUM; i++) {=0A=
+ pirq =3D platform_get_irq(info->pdev, i);=0A=
+ info->irq[i] =3D regmap_irq_get_virq(info->regmap_irqc, pir=q);=0A=
+ if (info->irq[i] < 0) {=0A=
+ dev_warn(&info->pdev->dev,=0A=
+ "regmap_irq get virq failed for IRQ %d: %d\=
n",=0A=
+ pirq, info->irq[i]);=0A=
+ info->irq[i] =3D -1;=0A=
+ goto intr_failed;=0A=
+ }=0A=
+ ret =3D request_threaded_irq(info->irq[i],=0A=
+ NULL, fuel_gauge_thread_handler,=0A=
+ IRQF_ONESHOT, DEV_NAME, info);=0A=
+ if (ret) {=0A=
+ dev_warn(&info->pdev->dev,=0A=
+ "request irq failed for IRQ %d: %d\n",=0A=
+ pirq, info->irq[i]);=0A=
+ info->irq[i] =3D -1;=0A=
+ goto intr_failed;=0A=
+ } else {=0A=
+ dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n"=
,=0A=
+ pirq, info->irq[i]);=0A=
+ }=0A=
+ }=0A=
+ return;=0A=
+=0A=
+intr_failed:=0A=
+ for (; i > 0; i--) {=0A=
+ free_irq(info->irq[i - 1], info);=0A=
+ info->irq[i - 1] =3D -1;=0A=
+ }=0A=
+}=0A=
+=0A=
+static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info)=0A= +{=0A=
+ int ret;=0A=
+ unsigned int val;=0A=
+=0A= + ret =3D fuel_gauge_set_high_btemp_alert(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", r= et);=0A= +=0A= + ret =3D fuel_gauge_set_low_btemp_alert(info);=0A= + if (ret < 0)=0A= + dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", re= t);=0A= +=0A= + /* enable interrupts */=0A= + val =3D fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN);=0A= + val |=3D TEMP_IRQ_CFG_MASK;=0A= + fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val);=0A= +=0A= + val =3D fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN);=0A= + val |=3D FG_IRQ_CFG_LOWBATT_MASK;=0A= + val =3D fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val);=0A=
+}=0A=
+=0A=
+static int axp288_fuel_gauge_probe(struct platform_device *pdev)=0A= +{=0A=
+ int ret;=0A= + struct axp288_fg_info *info;=0A= + struct axp20x_dev *axp20x =3D dev_get_drvdata(pdev->dev.parent);=0A= +=0A= + info =3D devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);=0A= + if (!info)=0A= + return -ENOMEM;=0A= +=0A= + info->pdev =3D pdev;=0A= + info->regmap =3D axp20x->regmap;=0A= + info->regmap_irqc =3D axp20x->regmap_irqc;=0A= + info->status =3D POWER_SUPPLY_STATUS_UNKNOWN;=0A= + info->pdata =3D pdev->dev.platform_data;=0A= + if (!info->pdata)=0A= + return -ENODEV;=0A= +=0A= + platform_set_drvdata(pdev, info);=0A= +=0A= + mutex_init(&info->lock);=0A= + INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor)= ;=0A= +=0A= + info->bat.name =3D DEV_NAME;=0A= + info->bat.type =3D POWER_SUPPLY_TYPE_BATTERY;=0A= + info->bat.properties =3D fuel_gauge_props;=0A= + info->bat.num_properties =3D ARRAY_SIZE(fuel_gauge_props);=0A= + info->bat.get_property =3D fuel_gauge_get_property;=0A= + info->bat.set_property =3D fuel_gauge_set_property;=0A= + info->bat.property_is_writeable =3D fuel_gauge_property_is_writeabl=
e;=0A=
+ info->bat.external_power_changed =3D fuel_gauge_external_power_chan=
ged;=0A=
+ ret =3D power_supply_register(&pdev->dev, &info->bat);=0A=
+ if (ret) {=0A=
+ dev_err(&pdev->dev, "failed to register battery: %d\n", ret=
);=0A=
+ return ret;=0A=
+ }=0A=
+=0A= + fuel_gauge_create_debugfs(info);=0A= + fuel_gauge_init_config_regs(info);=0A= + fuel_gauge_init_irq(info);=0A= + fuel_gauge_init_hw_regs(info);=0A= + schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFI= ES);=0A= +=0A= + return ret;=0A=
+}=0A=
+=0A=
+static struct platform_device_id axp288_fg_id_table[] =3D {=0A=
+ { .name =3D DEV_NAME },=0A=
+ {},=0A=
+};=0A=
+=0A=
+static int axp288_fuel_gauge_remove(struct platform_device *pdev)=0A= +{=0A=
+ struct axp288_fg_info *info =3D platform_get_drvdata(pdev);=0A=
+ int i;=0A=
+=0A=
+ cancel_delayed_work_sync(&info->status_monitor);=0A=
+ power_supply_unregister(&info->bat);=0A=
+ fuel_gauge_remove_debugfs(info);=0A=
+=0A=
+ for (i =3D 0; i < AXP288_FG_INTR_NUM; i++)=0A=
+ if (info->irq[i] >=3D 0)=0A=
+ free_irq(info->irq[i], info);=0A=
+=0A=
+ return 0;=0A=
+}=0A=
+=0A=
+static struct platform_driver axp288_fuel_gauge_driver =3D {=0A=
+ .probe =3D axp288_fuel_gauge_probe,=0A=
+ .remove =3D axp288_fuel_gauge_remove,=0A=
+ .id_table =3D axp288_fg_id_table,=0A=
+ .driver =3D {=0A=+ .name =3D DEV_NAME,=0A=
+ },=0A=
+};=0A=
+=0A= +module_platform_driver(axp288_fuel_gauge_driver);=0A= +=0A=
+MODULE_AUTHOR("Todd Brandt <[email protected]>");=0A= +MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");=0A= +MODULE_LICENSE("GPL");=0A=
--=0A=
1.9.1=0A=
=0A=
Hi,
Quoted 1 line · Todd E
Hi Dmitry and Sebastian, can we get this driver included for 3.20?
Since the merge window is near I did not plan to take any more patches for 3.20 (except fixes). Apart from that the patches do not apply cleanly to battery-2.6's master branch.
-- Sebastian