linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* 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).