* RE: [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver
2015-02-02 23:41 ` [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
@ 2015-02-02 20:57 ` Brandt, Todd E
2015-02-04 15:07 ` sre
0 siblings, 1 reply; 7+ messages in thread
From: Brandt, Todd E @ 2015-02-02 20:57 UTC (permalink / raw)
To: Todd Brandt, sre@kernel.org, dbaryshkov@gmail.com,
Woodhouse, David
Cc: linux-pm@vger.kernel.org, linux-iio@vger.kernel.org,
linux-kernel@vger.kernel.org, jacob.jun.pan@linux.intel.com,
sameo@linux.intel.com, lee.jones@linaro.org, dwmw2@infradead.org,
jic23@kernel.org
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 [todd.e.brandt@linux.intel.com]=0A=
Sent: Monday, February 02, 2015 12:44 PM=0A=
To: sre@kernel.org; dbaryshkov@gmail.com; Woodhouse, David=0A=
Cc: linux-pm@vger.kernel.org; linux-iio@vger.kernel.org; linux-kernel@vger.=
kernel.org; todd.e.brandt@linux.intel.com; Brandt, Todd E; jacob.jun.pan@li=
nux.intel.com; sameo@linux.intel.com; lee.jones@linaro.org; dwmw2@infradead=
.org; jic23@kernel.org=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=
Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>=0A=
Acked-by: Jacob Pan <jacob.jun.pan@linux.intel.com>=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=
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig=0A=
index 0108c2a..a1166e0 100644=0A=
--- a/drivers/power/Kconfig=0A=
+++ b/drivers/power/Kconfig=0A=
@@ -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=
diff --git a/drivers/power/Makefile b/drivers/power/Makefile=0A=
index dfa8942..eb76b23 100644=0A=
--- a/drivers/power/Makefile=0A=
+++ b/drivers/power/Makefile=0A=
@@ -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=
diff --git a/drivers/power/axp288_fuel_gauge.c b/drivers/power/axp288_fuel_=
gauge.c=0A=
new file mode 100644=0A=
index 0000000..c86e709=0A=
--- /dev/null=0A=
+++ b/drivers/power/axp288_fuel_gauge.c=0A=
@@ -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 >=
> 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 >=
> 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 <todd.e.brandt@linux.intel.com>");=0A=
+MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");=0A=
+MODULE_LICENSE("GPL");=0A=
--=0A=
1.9.1=0A=
=0A=
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 0/3] X-Power AXP288 PMIC Fuel Gauge Driver
@ 2015-02-02 23:41 Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge Todd Brandt
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Todd Brandt @ 2015-02-02 23:41 UTC (permalink / raw)
To: sre, dbaryshkov, david.woodhouse
Cc: linux-pm, linux-iio, linux-kernel, todd.e.brandt, todd.e.brandt,
jacob.jun.pan, sameo, lee.jones, dwmw2, jic23
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
--
1.9.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge
2015-02-02 23:41 [PATCH RESEND v3 0/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
@ 2015-02-02 23:41 ` Todd Brandt
2015-02-16 13:54 ` Lee Jones
2015-02-02 23:41 ` [PATCH RESEND v3 2/3] mfd/axp20x: add support for fuel gauge cell Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2 siblings, 1 reply; 7+ messages in thread
From: Todd Brandt @ 2015-02-02 23:41 UTC (permalink / raw)
To: sre, dbaryshkov, david.woodhouse
Cc: linux-pm, linux-iio, linux-kernel, todd.e.brandt, todd.e.brandt,
jacob.jun.pan, sameo, lee.jones, dwmw2, jic23
Name changes to the battery cell structure to a
more generic cell type: fuel gauge.
Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Acked-By: Sebastian Reichel <sre@kernel.org>
Acked-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
---
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
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index b1b580a..0acbe52 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -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",
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 2/3] mfd/axp20x: add support for fuel gauge cell
2015-02-02 23:41 [PATCH RESEND v3 0/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge Todd Brandt
@ 2015-02-02 23:41 ` Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2 siblings, 0 replies; 7+ messages in thread
From: Todd Brandt @ 2015-02-02 23:41 UTC (permalink / raw)
To: sre, dbaryshkov, david.woodhouse
Cc: linux-pm, linux-iio, linux-kernel, todd.e.brandt, todd.e.brandt,
jacob.jun.pan, sameo, lee.jones, dwmw2, jic23
Register definitions and platform data structure
for fuel gauge cell devices.
Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Acked-By: Sebastian Reichel <sre@kernel.org>
Acked-By: Jacob Pan <jacob.jun.pan@linux.intel.com>
Acked-By: Lee Jones <lee.jones@linaro.org>
---
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
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 81589d1..dfabd6d 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -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 */
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver
2015-02-02 23:41 [PATCH RESEND v3 0/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 2/3] mfd/axp20x: add support for fuel gauge cell Todd Brandt
@ 2015-02-02 23:41 ` Todd Brandt
2015-02-02 20:57 ` Brandt, Todd E
2 siblings, 1 reply; 7+ messages in thread
From: Todd Brandt @ 2015-02-02 23:41 UTC (permalink / raw)
To: sre, dbaryshkov, david.woodhouse
Cc: linux-pm, linux-iio, linux-kernel, todd.e.brandt, todd.e.brandt,
jacob.jun.pan, sameo, lee.jones, dwmw2, jic23
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
Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Acked-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
---
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
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 0108c2a..a1166e0 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -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
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index dfa8942..eb76b23 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -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
diff --git a/drivers/power/axp288_fuel_gauge.c b/drivers/power/axp288_fuel_gauge.c
new file mode 100644
index 0000000..c86e709
--- /dev/null
+++ b/drivers/power/axp288_fuel_gauge.c
@@ -0,0 +1,1151 @@
+/*
+ * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/iio/consumer.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
+#define CHRG_STAT_BAT_VALID (1 << 4)
+#define CHRG_STAT_BAT_PRESENT (1 << 5)
+#define CHRG_STAT_CHARGING (1 << 6)
+#define CHRG_STAT_PMIC_OTP (1 << 7)
+
+#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */
+#define CHRG_CCCV_CC_BIT_POS 0
+#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */
+#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
+#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */
+#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */
+#define CHRG_CCCV_CV_BIT_POS 5
+#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */
+#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */
+#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
+#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
+#define CHRG_CCCV_CHG_EN (1 << 7)
+
+#define CV_4100 4100 /* 4100mV */
+#define CV_4150 4150 /* 4150mV */
+#define CV_4200 4200 /* 4200mV */
+#define CV_4350 4350 /* 4350mV */
+
+#define TEMP_IRQ_CFG_QWBTU (1 << 0)
+#define TEMP_IRQ_CFG_WBTU (1 << 1)
+#define TEMP_IRQ_CFG_QWBTO (1 << 2)
+#define TEMP_IRQ_CFG_WBTO (1 << 3)
+#define TEMP_IRQ_CFG_MASK 0xf
+
+#define FG_IRQ_CFG_LOWBATT_WL2 (1 << 0)
+#define FG_IRQ_CFG_LOWBATT_WL1 (1 << 1)
+#define FG_IRQ_CFG_LOWBATT_MASK 0x3
+#define LOWBAT_IRQ_STAT_LOWBATT_WL2 (1 << 0)
+#define LOWBAT_IRQ_STAT_LOWBATT_WL1 (1 << 1)
+
+#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
+#define FG_CNTL_OCV_ADJ_EN (1 << 3)
+#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
+#define FG_CNTL_CAP_ADJ_EN (1 << 5)
+#define FG_CNTL_CC_EN (1 << 6)
+#define FG_CNTL_GAUGE_EN (1 << 7)
+
+#define FG_REP_CAP_VALID (1 << 7)
+#define FG_REP_CAP_VAL_MASK 0x7F
+
+#define FG_DES_CAP1_VALID (1 << 7)
+#define FG_DES_CAP1_VAL_MASK 0x7F
+#define FG_DES_CAP0_VAL_MASK 0xFF
+#define FG_DES_CAP_RES_LSB 1456 /* 1.456mAhr */
+
+#define FG_CC_MTR1_VALID (1 << 7)
+#define FG_CC_MTR1_VAL_MASK 0x7F
+#define FG_CC_MTR0_VAL_MASK 0xFF
+#define FG_DES_CC_RES_LSB 1456 /* 1.456mAhr */
+
+#define FG_OCV_CAP_VALID (1 << 7)
+#define FG_OCV_CAP_VAL_MASK 0x7F
+#define FG_CC_CAP_VALID (1 << 7)
+#define FG_CC_CAP_VAL_MASK 0x7F
+
+#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
+#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */
+#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
+#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */
+#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */
+#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */
+
+#define STATUS_MON_DELAY_JIFFIES (HZ * 60) /*60 sec */
+#define NR_RETRY_CNT 3
+#define DEV_NAME "axp288_fuel_gauge"
+
+/* 1.1mV per LSB expressed in uV */
+#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
+/* properties converted to tenths of degrees, uV, uA, uW */
+#define PROP_TEMP(a) ((a) * 10)
+#define UNPROP_TEMP(a) ((a) / 10)
+#define PROP_VOLT(a) ((a) * 1000)
+#define PROP_CURR(a) ((a) * 1000)
+
+#define AXP288_FG_INTR_NUM 6
+enum {
+ QWBTU_IRQ = 0,
+ WBTU_IRQ,
+ QWBTO_IRQ,
+ WBTO_IRQ,
+ WL2_IRQ,
+ WL1_IRQ,
+};
+
+struct axp288_fg_info {
+ struct platform_device *pdev;
+ struct axp20x_fg_pdata *pdata;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+ int irq[AXP288_FG_INTR_NUM];
+ struct power_supply bat;
+ struct mutex lock;
+ int status;
+ struct delayed_work status_monitor;
+ struct dentry *debug_file;
+};
+
+static enum power_supply_property fuel_gauge_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_MAX,
+ POWER_SUPPLY_PROP_TEMP_MIN,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
+{
+ int ret, i;
+ unsigned int val;
+
+ for (i = 0; i < NR_RETRY_CNT; i++) {
+ ret = regmap_read(info->regmap, reg, &val);
+ if (ret == -EBUSY)
+ continue;
+ else
+ break;
+ }
+
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
+
+ return val;
+}
+
+static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
+{
+ int ret;
+
+ ret = regmap_write(info->regmap, reg, (unsigned int)val);
+
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
+
+ return ret;
+}
+
+static int pmic_read_adc_val(const char *name, int *raw_val,
+ struct axp288_fg_info *info)
+{
+ int ret, val = 0;
+ struct iio_channel *indio_chan;
+
+ indio_chan = iio_channel_get(NULL, name);
+ if (IS_ERR_OR_NULL(indio_chan)) {
+ ret = PTR_ERR(indio_chan);
+ goto exit;
+ }
+ ret = iio_read_channel_raw(indio_chan, &val);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev,
+ "IIO channel read error: %x, %x\n", ret, val);
+ goto err_exit;
+ }
+
+ dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val);
+ *raw_val = val;
+
+err_exit:
+ iio_channel_release(indio_chan);
+exit:
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int fuel_gauge_debug_show(struct seq_file *s, void *data)
+{
+ struct axp288_fg_info *info = s->private;
+ int raw_val, ret;
+
+ seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
+ AXP20X_PWR_INPUT_STATUS,
+ fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
+ seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
+ AXP20X_PWR_OP_MODE,
+ fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
+ seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
+ AXP20X_CHRG_CTRL1,
+ fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
+ seq_printf(s, " VLTF[%02x] : %02x\n",
+ AXP20X_V_LTF_DISCHRG,
+ fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
+ seq_printf(s, " VHTF[%02x] : %02x\n",
+ AXP20X_V_HTF_DISCHRG,
+ fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
+ seq_printf(s, " CC_CTRL[%02x] : %02x\n",
+ AXP20X_CC_CTRL,
+ fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
+ seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
+ AXP20X_FG_RES,
+ fuel_gauge_reg_readb(info, AXP20X_FG_RES));
+ seq_printf(s, " FG_RDC1[%02x] : %02x\n",
+ AXP288_FG_RDC1_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
+ seq_printf(s, " FG_RDC0[%02x] : %02x\n",
+ AXP288_FG_RDC0_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
+ seq_printf(s, " FG_OCVH[%02x] : %02x\n",
+ AXP288_FG_OCVH_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));
+ seq_printf(s, " FG_OCVL[%02x] : %02x\n",
+ AXP288_FG_OCVL_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));
+ seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",
+ AXP288_FG_DES_CAP1_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG));
+ seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",
+ AXP288_FG_DES_CAP0_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG));
+ seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n",
+ AXP288_FG_CC_MTR1_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG));
+ seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n",
+ AXP288_FG_CC_MTR0_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG));
+ seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
+ AXP288_FG_OCV_CAP_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
+ seq_printf(s, " FG_CC_CAP[%02x] : %02x\n",
+ AXP288_FG_CC_CAP_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
+ seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
+ AXP288_FG_LOW_CAP_REG,
+ fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
+ seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
+ AXP288_FG_TUNE0,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
+ seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
+ AXP288_FG_TUNE1,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
+ seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
+ AXP288_FG_TUNE2,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
+ seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
+ AXP288_FG_TUNE3,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
+ seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
+ AXP288_FG_TUNE4,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
+ seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
+ AXP288_FG_TUNE5,
+ fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
+
+ ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-batttemp : %d\n", raw_val);
+ ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
+ ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-systtemp : %d\n", raw_val);
+ ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
+ ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
+ ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
+ if (ret >= 0)
+ seq_printf(s, "axp288-battvolt : %d\n", raw_val);
+
+ return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fuel_gauge_debug_show, inode->i_private);
+}
+
+static const struct file_operations fg_debug_fops = {
+ .open = debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
+{
+ info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
+ info, &fg_debug_fops);
+}
+
+static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
+{
+ debugfs_remove(info->debug_file);
+}
+#else
+static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
+{
+}
+static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
+{
+}
+#endif
+
+static void fuel_gauge_get_status(struct axp288_fg_info *info)
+{
+ int pwr_stat, ret;
+ int charge, discharge;
+
+ pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
+ if (pwr_stat < 0) {
+ dev_err(&info->pdev->dev,
+ "PWR STAT read failed:%d\n", pwr_stat);
+ return;
+ }
+ ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev,
+ "ADC charge current read failed:%d\n", ret);
+ return;
+ }
+ ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev,
+ "ADC discharge current read failed:%d\n", ret);
+ return;
+ }
+
+ if (charge > 0)
+ info->status = POWER_SUPPLY_STATUS_CHARGING;
+ else if (discharge > 0)
+ info->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else {
+ if (pwr_stat & CHRG_STAT_BAT_PRESENT)
+ info->status = POWER_SUPPLY_STATUS_FULL;
+ else
+ info->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+}
+
+static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
+{
+ int ret = 0, raw_val;
+
+ ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
+ if (ret < 0)
+ goto vbatt_read_fail;
+
+ *vbatt = VOLTAGE_FROM_ADC(raw_val);
+vbatt_read_fail:
+ return ret;
+}
+
+static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
+{
+ int ret, value = 0;
+ int charge, discharge;
+
+ ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
+ if (ret < 0)
+ goto current_read_fail;
+ ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
+ if (ret < 0)
+ goto current_read_fail;
+
+ if (charge > 0)
+ value = charge;
+ else if (discharge > 0)
+ value = -1 * discharge;
+
+ *cur = value;
+current_read_fail:
+ return ret;
+}
+
+static int temp_to_adc(struct axp288_fg_info *info, int tval)
+{
+ int rntc = 0, i, ret, adc_val;
+ int rmin, rmax, tmin, tmax;
+ int tcsz = info->pdata->tcsz;
+
+ /* get the Rntc resitance value for this temp */
+ if (tval > info->pdata->thermistor_curve[0][1]) {
+ rntc = info->pdata->thermistor_curve[0][0];
+ } else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) {
+ rntc = info->pdata->thermistor_curve[tcsz-1][0];
+ } else {
+ for (i = 1; i < tcsz; i++) {
+ if (tval > info->pdata->thermistor_curve[i][1]) {
+ rmin = info->pdata->thermistor_curve[i-1][0];
+ rmax = info->pdata->thermistor_curve[i][0];
+ tmin = info->pdata->thermistor_curve[i-1][1];
+ tmax = info->pdata->thermistor_curve[i][1];
+ rntc = rmin + ((rmax - rmin) *
+ (tval - tmin) / (tmax - tmin));
+ break;
+ }
+ }
+ }
+
+ /* we need the current to calculate the proper adc voltage */
+ ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
+ ret = 0x30;
+ }
+
+ /*
+ * temperature is proportional to NTS thermistor resistance
+ * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
+ * [12-bit ADC VAL] = R_NTC(Ω) * current / 800
+ */
+ adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800;
+
+ return adc_val;
+}
+
+static int adc_to_temp(struct axp288_fg_info *info, int adc_val)
+{
+ int ret, r, i, tval = 0;
+ int rmin, rmax, tmin, tmax;
+ int tcsz = info->pdata->tcsz;
+
+ ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
+ ret = 0x30;
+ }
+
+ /*
+ * temperature is proportional to NTS thermistor resistance
+ * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
+ * R_NTC(Ω) = [12-bit ADC VAL] * 800 / current
+ */
+ r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3)));
+
+ if (r < info->pdata->thermistor_curve[0][0]) {
+ tval = info->pdata->thermistor_curve[0][1];
+ } else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) {
+ tval = info->pdata->thermistor_curve[tcsz-1][1];
+ } else {
+ for (i = 1; i < tcsz; i++) {
+ if (r < info->pdata->thermistor_curve[i][0]) {
+ rmin = info->pdata->thermistor_curve[i-1][0];
+ rmax = info->pdata->thermistor_curve[i][0];
+ tmin = info->pdata->thermistor_curve[i-1][1];
+ tmax = info->pdata->thermistor_curve[i][1];
+ tval = tmin + ((tmax - tmin) *
+ (r - rmin) / (rmax - rmin));
+ break;
+ }
+ }
+ }
+
+ return tval;
+}
+
+static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp)
+{
+ int ret, raw_val = 0;
+
+ ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
+ if (ret < 0)
+ goto temp_read_fail;
+
+ *btemp = adc_to_temp(info, raw_val);
+
+temp_read_fail:
+ return ret;
+}
+
+static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
+{
+ int ret, value;
+
+ /* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);
+ if (ret < 0)
+ goto vocv_read_fail;
+ value = ret << 4;
+
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);
+ if (ret < 0)
+ goto vocv_read_fail;
+ value |= (ret & 0xf);
+
+ *vocv = VOLTAGE_FROM_ADC(value);
+vocv_read_fail:
+ return ret;
+}
+
+static int fuel_gauge_battery_health(struct axp288_fg_info *info)
+{
+ int temp, vocv;
+ int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN;
+
+ ret = fuel_gauge_get_btemp(info, &temp);
+ if (ret < 0)
+ goto health_read_fail;
+
+ ret = fuel_gauge_get_vocv(info, &vocv);
+ if (ret < 0)
+ goto health_read_fail;
+
+ if (vocv > info->pdata->max_volt)
+ health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else if (temp > info->pdata->max_temp)
+ health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (temp < info->pdata->min_temp)
+ health = POWER_SUPPLY_HEALTH_COLD;
+ else if (vocv < info->pdata->min_volt)
+ health = POWER_SUPPLY_HEALTH_DEAD;
+ else
+ health = POWER_SUPPLY_HEALTH_GOOD;
+
+health_read_fail:
+ return health;
+}
+
+static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info)
+{
+ int ret, adc_val;
+
+ /* program temperature threshold as 1/16 ADC value */
+ adc_val = temp_to_adc(info, info->pdata->max_temp);
+ ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4);
+
+ return ret;
+}
+
+static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info)
+{
+ int ret, adc_val;
+
+ /* program temperature threshold as 1/16 ADC value */
+ adc_val = temp_to_adc(info, info->pdata->min_temp);
+ ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4);
+
+ return ret;
+}
+
+static int fuel_gauge_get_property(struct power_supply *ps,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct axp288_fg_info *info = container_of(ps,
+ struct axp288_fg_info, bat);
+ int ret = 0, value;
+
+ mutex_lock(&info->lock);
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ fuel_gauge_get_status(info);
+ val->intval = info->status;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = fuel_gauge_battery_health(info);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = fuel_gauge_get_vbatt(info, &value);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ val->intval = PROP_VOLT(value);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ ret = fuel_gauge_get_vocv(info, &value);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ val->intval = PROP_VOLT(value);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = fuel_gauge_get_current(info, &value);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ val->intval = PROP_CURR(value);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+
+ if (ret & CHRG_STAT_BAT_PRESENT)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+
+ if (!(ret & FG_REP_CAP_VALID))
+ dev_err(&info->pdev->dev,
+ "capacity measurement not valid\n");
+ val->intval = (ret & FG_REP_CAP_VAL_MASK);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ val->intval = (ret & 0x0f);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ ret = fuel_gauge_get_btemp(info, &value);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ val->intval = PROP_TEMP(value);
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MAX:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ val->intval = PROP_TEMP(info->pdata->max_temp);
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ val->intval = PROP_TEMP(info->pdata->min_temp);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+
+ value = (ret & FG_CC_MTR1_VAL_MASK) << 8;
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ value |= (ret & FG_CC_MTR0_VAL_MASK);
+ val->intval = value * FG_DES_CAP_RES_LSB;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+
+ value = (ret & FG_DES_CAP1_VAL_MASK) << 8;
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG);
+ if (ret < 0)
+ goto fuel_gauge_read_err;
+ value |= (ret & FG_DES_CAP0_VAL_MASK);
+ val->intval = value * FG_DES_CAP_RES_LSB;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = PROP_CURR(info->pdata->design_cap);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = PROP_VOLT(info->pdata->max_volt);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = PROP_VOLT(info->pdata->min_volt);
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = info->pdata->battid;
+ break;
+ default:
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&info->lock);
+ return 0;
+
+fuel_gauge_read_err:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int fuel_gauge_set_property(struct power_supply *ps,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct axp288_fg_info *info = container_of(ps,
+ struct axp288_fg_info, bat);
+ int ret = 0;
+
+ mutex_lock(&info->lock);
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ info->status = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ if ((val->intval < PD_DEF_MIN_TEMP) ||
+ (val->intval > PD_DEF_MAX_TEMP)) {
+ ret = -EINVAL;
+ break;
+ }
+ info->pdata->min_temp = UNPROP_TEMP(val->intval);
+ ret = fuel_gauge_set_low_btemp_alert(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev,
+ "temp alert min set fail:%d\n", ret);
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MAX:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ if ((val->intval < PD_DEF_MIN_TEMP) ||
+ (val->intval > PD_DEF_MAX_TEMP)) {
+ ret = -EINVAL;
+ break;
+ }
+ info->pdata->max_temp = UNPROP_TEMP(val->intval);
+ ret = fuel_gauge_set_high_btemp_alert(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev,
+ "temp alert max set fail:%d\n", ret);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
+ if ((val->intval < 0) || (val->intval > 15)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
+ if (ret < 0)
+ break;
+ ret &= 0xf0;
+ ret |= (val->intval & 0xf);
+ ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int fuel_gauge_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_TEMP_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ case POWER_SUPPLY_PROP_TEMP_MAX:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void fuel_gauge_status_monitor(struct work_struct *work)
+{
+ struct axp288_fg_info *info = container_of(work,
+ struct axp288_fg_info, status_monitor.work);
+
+ fuel_gauge_get_status(info);
+ power_supply_changed(&info->bat);
+ schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
+}
+
+static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
+{
+ struct axp288_fg_info *info = dev;
+ int i;
+
+ for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
+ if (info->irq[i] == irq)
+ break;
+ }
+
+ if (i >= AXP288_FG_INTR_NUM) {
+ dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
+ return IRQ_NONE;
+ }
+
+ switch (i) {
+ case QWBTU_IRQ:
+ dev_info(&info->pdev->dev,
+ "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
+ break;
+ case WBTU_IRQ:
+ dev_info(&info->pdev->dev,
+ "Battery under temperature in work mode IRQ (WBTU)\n");
+ break;
+ case QWBTO_IRQ:
+ dev_info(&info->pdev->dev,
+ "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
+ break;
+ case WBTO_IRQ:
+ dev_info(&info->pdev->dev,
+ "Battery over temperature in work mode IRQ (WBTO)\n");
+ break;
+ case WL2_IRQ:
+ dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
+ break;
+ case WL1_IRQ:
+ dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
+ break;
+ default:
+ dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
+ }
+
+ power_supply_changed(&info->bat);
+ return IRQ_HANDLED;
+}
+
+static void fuel_gauge_external_power_changed(struct power_supply *psy)
+{
+ struct axp288_fg_info *info = container_of(psy,
+ struct axp288_fg_info, bat);
+
+ power_supply_changed(&info->bat);
+}
+
+static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info)
+{
+ int ret;
+ u8 reg_val;
+
+ ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
+ return ret;
+ }
+ ret = (ret & FG_REP_CAP_VAL_MASK);
+
+ if (ret > FG_LOW_CAP_WARN_THR)
+ reg_val = FG_LOW_CAP_WARN_THR;
+ else if (ret > FG_LOW_CAP_CRIT_THR)
+ reg_val = FG_LOW_CAP_CRIT_THR;
+ else
+ reg_val = FG_LOW_CAP_SHDN_THR;
+
+ reg_val |= FG_LOW_CAP_THR1_VAL;
+ ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info)
+{
+ int ret;
+ u8 val;
+
+ ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
+ if (ret < 0)
+ goto fg_prog_ocv_fail;
+ else
+ val = (ret & ~CHRG_CCCV_CV_MASK);
+
+ switch (info->pdata->max_volt) {
+ case CV_4100:
+ val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);
+ break;
+ case CV_4150:
+ val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);
+ break;
+ case CV_4200:
+ val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
+ break;
+ case CV_4350:
+ val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);
+ break;
+ default:
+ val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
+ break;
+ }
+
+ ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val);
+fg_prog_ocv_fail:
+ return ret;
+}
+
+static int fuel_gauge_program_design_cap(struct axp288_fg_info *info)
+{
+ int ret;
+
+ ret = fuel_gauge_reg_writeb(info,
+ AXP288_FG_DES_CAP1_REG, info->pdata->cap1);
+ if (ret < 0)
+ goto fg_prog_descap_fail;
+
+ ret = fuel_gauge_reg_writeb(info,
+ AXP288_FG_DES_CAP0_REG, info->pdata->cap0);
+
+fg_prog_descap_fail:
+ return ret;
+}
+
+static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info)
+{
+ int ret = 0, i;
+
+ for (i = 0; i < OCV_CURVE_SIZE; i++) {
+ ret = fuel_gauge_reg_writeb(info,
+ AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]);
+ if (ret < 0)
+ goto fg_prog_ocv_fail;
+ }
+
+fg_prog_ocv_fail:
+ return ret;
+}
+
+static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info)
+{
+ int ret;
+
+ ret = fuel_gauge_reg_writeb(info,
+ AXP288_FG_RDC1_REG, info->pdata->rdc1);
+ if (ret < 0)
+ goto fg_prog_ocv_fail;
+
+ ret = fuel_gauge_reg_writeb(info,
+ AXP288_FG_RDC0_REG, info->pdata->rdc0);
+
+fg_prog_ocv_fail:
+ return ret;
+}
+
+static void fuel_gauge_init_config_regs(struct axp288_fg_info *info)
+{
+ int ret;
+
+ /*
+ * check if the config data is already
+ * programmed and if so just return.
+ */
+
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ if (ret < 0) {
+ dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");
+ } else if (!(ret & FG_DES_CAP1_VALID)) {
+ dev_info(&info->pdev->dev, "FG data needs to be initialized\n");
+ } else {
+ dev_info(&info->pdev->dev, "FG data is already initialized\n");
+ return;
+ }
+
+ ret = fuel_gauge_program_vbatt_full(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);
+
+ ret = fuel_gauge_program_design_cap(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);
+
+ ret = fuel_gauge_program_rdc_vals(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);
+
+ ret = fuel_gauge_program_ocv_curve(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);
+
+ ret = fuel_gauge_set_lowbatt_thresholds(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);
+
+ ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);
+}
+
+static void fuel_gauge_init_irq(struct axp288_fg_info *info)
+{
+ int ret, i, pirq;
+
+ for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
+ pirq = platform_get_irq(info->pdev, i);
+ info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
+ if (info->irq[i] < 0) {
+ dev_warn(&info->pdev->dev,
+ "regmap_irq get virq failed for IRQ %d: %d\n",
+ pirq, info->irq[i]);
+ info->irq[i] = -1;
+ goto intr_failed;
+ }
+ ret = request_threaded_irq(info->irq[i],
+ NULL, fuel_gauge_thread_handler,
+ IRQF_ONESHOT, DEV_NAME, info);
+ if (ret) {
+ dev_warn(&info->pdev->dev,
+ "request irq failed for IRQ %d: %d\n",
+ pirq, info->irq[i]);
+ info->irq[i] = -1;
+ goto intr_failed;
+ } else {
+ dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
+ pirq, info->irq[i]);
+ }
+ }
+ return;
+
+intr_failed:
+ for (; i > 0; i--) {
+ free_irq(info->irq[i - 1], info);
+ info->irq[i - 1] = -1;
+ }
+}
+
+static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info)
+{
+ int ret;
+ unsigned int val;
+
+ ret = fuel_gauge_set_high_btemp_alert(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret);
+
+ ret = fuel_gauge_set_low_btemp_alert(info);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret);
+
+ /* enable interrupts */
+ val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN);
+ val |= TEMP_IRQ_CFG_MASK;
+ fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val);
+
+ val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN);
+ val |= FG_IRQ_CFG_LOWBATT_MASK;
+ val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val);
+}
+
+static int axp288_fuel_gauge_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct axp288_fg_info *info;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdev = pdev;
+ info->regmap = axp20x->regmap;
+ info->regmap_irqc = axp20x->regmap_irqc;
+ info->status = POWER_SUPPLY_STATUS_UNKNOWN;
+ info->pdata = pdev->dev.platform_data;
+ if (!info->pdata)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, info);
+
+ mutex_init(&info->lock);
+ INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
+
+ info->bat.name = DEV_NAME;
+ info->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ info->bat.properties = fuel_gauge_props;
+ info->bat.num_properties = ARRAY_SIZE(fuel_gauge_props);
+ info->bat.get_property = fuel_gauge_get_property;
+ info->bat.set_property = fuel_gauge_set_property;
+ info->bat.property_is_writeable = fuel_gauge_property_is_writeable;
+ info->bat.external_power_changed = fuel_gauge_external_power_changed;
+ ret = power_supply_register(&pdev->dev, &info->bat);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
+ return ret;
+ }
+
+ fuel_gauge_create_debugfs(info);
+ fuel_gauge_init_config_regs(info);
+ fuel_gauge_init_irq(info);
+ fuel_gauge_init_hw_regs(info);
+ schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
+
+ return ret;
+}
+
+static struct platform_device_id axp288_fg_id_table[] = {
+ { .name = DEV_NAME },
+ {},
+};
+
+static int axp288_fuel_gauge_remove(struct platform_device *pdev)
+{
+ struct axp288_fg_info *info = platform_get_drvdata(pdev);
+ int i;
+
+ cancel_delayed_work_sync(&info->status_monitor);
+ power_supply_unregister(&info->bat);
+ fuel_gauge_remove_debugfs(info);
+
+ for (i = 0; i < AXP288_FG_INTR_NUM; i++)
+ if (info->irq[i] >= 0)
+ free_irq(info->irq[i], info);
+
+ return 0;
+}
+
+static struct platform_driver axp288_fuel_gauge_driver = {
+ .probe = axp288_fuel_gauge_probe,
+ .remove = axp288_fuel_gauge_remove,
+ .id_table = axp288_fg_id_table,
+ .driver = {
+ .name = DEV_NAME,
+ },
+};
+
+module_platform_driver(axp288_fuel_gauge_driver);
+
+MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
+MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver
2015-02-02 20:57 ` Brandt, Todd E
@ 2015-02-04 15:07 ` sre
0 siblings, 0 replies; 7+ messages in thread
From: sre @ 2015-02-04 15:07 UTC (permalink / raw)
To: Brandt, Todd E
Cc: Todd Brandt, dbaryshkov@gmail.com, Woodhouse, David,
linux-pm@vger.kernel.org, linux-iio@vger.kernel.org,
linux-kernel@vger.kernel.org, jacob.jun.pan@linux.intel.com,
sameo@linux.intel.com, lee.jones@linaro.org, dwmw2@infradead.org,
jic23@kernel.org
[-- Attachment #1: Type: text/plain, Size: 330 bytes --]
Hi,
On Mon, Feb 02, 2015 at 08:57:35PM +0000, Brandt, Todd E wrote:
> 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
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge
2015-02-02 23:41 ` [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge Todd Brandt
@ 2015-02-16 13:54 ` Lee Jones
0 siblings, 0 replies; 7+ messages in thread
From: Lee Jones @ 2015-02-16 13:54 UTC (permalink / raw)
To: Todd Brandt
Cc: sre, dbaryshkov, david.woodhouse, linux-pm, linux-iio,
linux-kernel, todd.e.brandt, jacob.jun.pan, sameo, dwmw2, jic23
On Mon, 02 Feb 2015, Todd Brandt wrote:
> Name changes to the battery cell structure to a
> more generic cell type: fuel gauge.
>
> Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> Acked-By: Sebastian Reichel <sre@kernel.org>
> Acked-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
> ---
> drivers/mfd/axp20x.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
Applied, thanks.
> v1: 01/07/14
> v2: 01/22/14
> - replaced the const in axp20x_acpi_match declaration
> v3: same as v2
FYI: Changelogs are usually placed _above_ the diff stat.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-02-16 13:54 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-02 23:41 [PATCH RESEND v3 0/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 1/3] mfd/axp20x: change battery cell name to fuel gauge Todd Brandt
2015-02-16 13:54 ` Lee Jones
2015-02-02 23:41 ` [PATCH RESEND v3 2/3] mfd/axp20x: add support for fuel gauge cell Todd Brandt
2015-02-02 23:41 ` [PATCH RESEND v3 3/3] X-Power AXP288 PMIC Fuel Gauge Driver Todd Brandt
2015-02-02 20:57 ` Brandt, Todd E
2015-02-04 15:07 ` sre
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).